roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         isIOS = /iphone|ipad/.test(ua),
67         isTouch =  (function() {
68             try {  
69                 document.createEvent("TouchEvent");  
70                 return true;  
71             } catch (e) {  
72                 return false;  
73             } 
74             
75         })();
76     // remove css image flicker
77         if(isIE && !isIE7){
78         try{
79             document.execCommand("BackgroundImageCache", false, true);
80         }catch(e){}
81     }
82     
83     Roo.apply(Roo, {
84         /**
85          * True if the browser is in strict mode
86          * @type Boolean
87          */
88         isStrict : isStrict,
89         /**
90          * True if the page is running over SSL
91          * @type Boolean
92          */
93         isSecure : isSecure,
94         /**
95          * True when the document is fully initialized and ready for action
96          * @type Boolean
97          */
98         isReady : false,
99         /**
100          * Turn on debugging output (currently only the factory uses this)
101          * @type Boolean
102          */
103         
104         debug: false,
105
106         /**
107          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
108          * @type Boolean
109          */
110         enableGarbageCollector : true,
111
112         /**
113          * True to automatically purge event listeners after uncaching an element (defaults to false).
114          * Note: this only happens if enableGarbageCollector is true.
115          * @type Boolean
116          */
117         enableListenerCollection:false,
118
119         /**
120          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
121          * the IE insecure content warning (defaults to javascript:false).
122          * @type String
123          */
124         SSL_SECURE_URL : "javascript:false",
125
126         /**
127          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
128          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
129          * @type String
130          */
131         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
132
133         emptyFn : function(){},
134         
135         /**
136          * Copies all the properties of config to obj if they don't already exist.
137          * @param {Object} obj The receiver of the properties
138          * @param {Object} config The source of the properties
139          * @return {Object} returns obj
140          */
141         applyIf : function(o, c){
142             if(o && c){
143                 for(var p in c){
144                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
145                 }
146             }
147             return o;
148         },
149
150         /**
151          * Applies event listeners to elements by selectors when the document is ready.
152          * The event name is specified with an @ suffix.
153 <pre><code>
154 Roo.addBehaviors({
155    // add a listener for click on all anchors in element with id foo
156    '#foo a@click' : function(e, t){
157        // do something
158    },
159
160    // add the same listener to multiple selectors (separated by comma BEFORE the @)
161    '#foo a, #bar span.some-class@mouseover' : function(){
162        // do something
163    }
164 });
165 </code></pre>
166          * @param {Object} obj The list of behaviors to apply
167          */
168         addBehaviors : function(o){
169             if(!Roo.isReady){
170                 Roo.onReady(function(){
171                     Roo.addBehaviors(o);
172                 });
173                 return;
174             }
175             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
176             for(var b in o){
177                 var parts = b.split('@');
178                 if(parts[1]){ // for Object prototype breakers
179                     var s = parts[0];
180                     if(!cache[s]){
181                         cache[s] = Roo.select(s);
182                     }
183                     cache[s].on(parts[1], o[b]);
184                 }
185             }
186             cache = null;
187         },
188
189         /**
190          * Generates unique ids. If the element already has an id, it is unchanged
191          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
192          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
193          * @return {String} The generated Id.
194          */
195         id : function(el, prefix){
196             prefix = prefix || "roo-gen";
197             el = Roo.getDom(el);
198             var id = prefix + (++idSeed);
199             return el ? (el.id ? el.id : (el.id = id)) : id;
200         },
201          
202        
203         /**
204          * Extends one class with another class and optionally overrides members with the passed literal. This class
205          * also adds the function "override()" to the class that can be used to override
206          * members on an instance.
207          * @param {Object} subclass The class inheriting the functionality
208          * @param {Object} superclass The class being extended
209          * @param {Object} overrides (optional) A literal with members
210          * @method extend
211          */
212         extend : function(){
213             // inline overrides
214             var io = function(o){
215                 for(var m in o){
216                     this[m] = o[m];
217                 }
218             };
219             return function(sb, sp, overrides){
220                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
221                     overrides = sp;
222                     sp = sb;
223                     sb = function(){sp.apply(this, arguments);};
224                 }
225                 var F = function(){}, sbp, spp = sp.prototype;
226                 F.prototype = spp;
227                 sbp = sb.prototype = new F();
228                 sbp.constructor=sb;
229                 sb.superclass=spp;
230                 
231                 if(spp.constructor == Object.prototype.constructor){
232                     spp.constructor=sp;
233                    
234                 }
235                 
236                 sb.override = function(o){
237                     Roo.override(sb, o);
238                 };
239                 sbp.override = io;
240                 Roo.override(sb, overrides);
241                 return sb;
242             };
243         }(),
244
245         /**
246          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
247          * Usage:<pre><code>
248 Roo.override(MyClass, {
249     newMethod1: function(){
250         // etc.
251     },
252     newMethod2: function(foo){
253         // etc.
254     }
255 });
256  </code></pre>
257          * @param {Object} origclass The class to override
258          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
259          * containing one or more methods.
260          * @method override
261          */
262         override : function(origclass, overrides){
263             if(overrides){
264                 var p = origclass.prototype;
265                 for(var method in overrides){
266                     p[method] = overrides[method];
267                 }
268             }
269         },
270         /**
271          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
272          * <pre><code>
273 Roo.namespace('Company', 'Company.data');
274 Company.Widget = function() { ... }
275 Company.data.CustomStore = function(config) { ... }
276 </code></pre>
277          * @param {String} namespace1
278          * @param {String} namespace2
279          * @param {String} etc
280          * @method namespace
281          */
282         namespace : function(){
283             var a=arguments, o=null, i, j, d, rt;
284             for (i=0; i<a.length; ++i) {
285                 d=a[i].split(".");
286                 rt = d[0];
287                 /** eval:var:o */
288                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
289                 for (j=1; j<d.length; ++j) {
290                     o[d[j]]=o[d[j]] || {};
291                     o=o[d[j]];
292                 }
293             }
294         },
295         /**
296          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
297          * <pre><code>
298 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
299 Roo.factory(conf, Roo.data);
300 </code></pre>
301          * @param {String} classname
302          * @param {String} namespace (optional)
303          * @method factory
304          */
305          
306         factory : function(c, ns)
307         {
308             // no xtype, no ns or c.xns - or forced off by c.xns
309             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
310                 return c;
311             }
312             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
313             if (c.constructor == ns[c.xtype]) {// already created...
314                 return c;
315             }
316             if (ns[c.xtype]) {
317                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
318                 var ret = new ns[c.xtype](c);
319                 ret.xns = false;
320                 return ret;
321             }
322             c.xns = false; // prevent recursion..
323             return c;
324         },
325          /**
326          * Logs to console if it can.
327          *
328          * @param {String|Object} string
329          * @method log
330          */
331         log : function(s)
332         {
333             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
334                 return; // alerT?
335             }
336             console.log(s);
337             
338         },
339         /**
340          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
341          * @param {Object} o
342          * @return {String}
343          */
344         urlEncode : function(o){
345             if(!o){
346                 return "";
347             }
348             var buf = [];
349             for(var key in o){
350                 var ov = o[key], k = Roo.encodeURIComponent(key);
351                 var type = typeof ov;
352                 if(type == 'undefined'){
353                     buf.push(k, "=&");
354                 }else if(type != "function" && type != "object"){
355                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
356                 }else if(ov instanceof Array){
357                     if (ov.length) {
358                             for(var i = 0, len = ov.length; i < len; i++) {
359                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
360                             }
361                         } else {
362                             buf.push(k, "=&");
363                         }
364                 }
365             }
366             buf.pop();
367             return buf.join("");
368         },
369          /**
370          * Safe version of encodeURIComponent
371          * @param {String} data 
372          * @return {String} 
373          */
374         
375         encodeURIComponent : function (data)
376         {
377             try {
378                 return encodeURIComponent(data);
379             } catch(e) {} // should be an uri encode error.
380             
381             if (data == '' || data == null){
382                return '';
383             }
384             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
385             function nibble_to_hex(nibble){
386                 var chars = '0123456789ABCDEF';
387                 return chars.charAt(nibble);
388             }
389             data = data.toString();
390             var buffer = '';
391             for(var i=0; i<data.length; i++){
392                 var c = data.charCodeAt(i);
393                 var bs = new Array();
394                 if (c > 0x10000){
395                         // 4 bytes
396                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
397                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
398                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
399                     bs[3] = 0x80 | (c & 0x3F);
400                 }else if (c > 0x800){
401                          // 3 bytes
402                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
403                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
404                     bs[2] = 0x80 | (c & 0x3F);
405                 }else if (c > 0x80){
406                        // 2 bytes
407                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
408                     bs[1] = 0x80 | (c & 0x3F);
409                 }else{
410                         // 1 byte
411                     bs[0] = c;
412                 }
413                 for(var j=0; j<bs.length; j++){
414                     var b = bs[j];
415                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
416                             + nibble_to_hex(b &0x0F);
417                     buffer += '%'+hex;
418                }
419             }
420             return buffer;    
421              
422         },
423
424         /**
425          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
426          * @param {String} string
427          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
428          * @return {Object} A literal with members
429          */
430         urlDecode : function(string, overwrite){
431             if(!string || !string.length){
432                 return {};
433             }
434             var obj = {};
435             var pairs = string.split('&');
436             var pair, name, value;
437             for(var i = 0, len = pairs.length; i < len; i++){
438                 pair = pairs[i].split('=');
439                 name = decodeURIComponent(pair[0]);
440                 value = decodeURIComponent(pair[1]);
441                 if(overwrite !== true){
442                     if(typeof obj[name] == "undefined"){
443                         obj[name] = value;
444                     }else if(typeof obj[name] == "string"){
445                         obj[name] = [obj[name]];
446                         obj[name].push(value);
447                     }else{
448                         obj[name].push(value);
449                     }
450                 }else{
451                     obj[name] = value;
452                 }
453             }
454             return obj;
455         },
456
457         /**
458          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
459          * passed array is not really an array, your function is called once with it.
460          * The supplied function is called with (Object item, Number index, Array allItems).
461          * @param {Array/NodeList/Mixed} array
462          * @param {Function} fn
463          * @param {Object} scope
464          */
465         each : function(array, fn, scope){
466             if(typeof array.length == "undefined" || typeof array == "string"){
467                 array = [array];
468             }
469             for(var i = 0, len = array.length; i < len; i++){
470                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
471             }
472         },
473
474         // deprecated
475         combine : function(){
476             var as = arguments, l = as.length, r = [];
477             for(var i = 0; i < l; i++){
478                 var a = as[i];
479                 if(a instanceof Array){
480                     r = r.concat(a);
481                 }else if(a.length !== undefined && !a.substr){
482                     r = r.concat(Array.prototype.slice.call(a, 0));
483                 }else{
484                     r.push(a);
485                 }
486             }
487             return r;
488         },
489
490         /**
491          * Escapes the passed string for use in a regular expression
492          * @param {String} str
493          * @return {String}
494          */
495         escapeRe : function(s) {
496             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
497         },
498
499         // internal
500         callback : function(cb, scope, args, delay){
501             if(typeof cb == "function"){
502                 if(delay){
503                     cb.defer(delay, scope, args || []);
504                 }else{
505                     cb.apply(scope, args || []);
506                 }
507             }
508         },
509
510         /**
511          * Return the dom node for the passed string (id), dom node, or Roo.Element
512          * @param {String/HTMLElement/Roo.Element} el
513          * @return HTMLElement
514          */
515         getDom : function(el){
516             if(!el){
517                 return null;
518             }
519             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
520         },
521
522         /**
523         * Shorthand for {@link Roo.ComponentMgr#get}
524         * @param {String} id
525         * @return Roo.Component
526         */
527         getCmp : function(id){
528             return Roo.ComponentMgr.get(id);
529         },
530          
531         num : function(v, defaultValue){
532             if(typeof v != 'number'){
533                 return defaultValue;
534             }
535             return v;
536         },
537
538         destroy : function(){
539             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
540                 var as = a[i];
541                 if(as){
542                     if(as.dom){
543                         as.removeAllListeners();
544                         as.remove();
545                         continue;
546                     }
547                     if(typeof as.purgeListeners == 'function'){
548                         as.purgeListeners();
549                     }
550                     if(typeof as.destroy == 'function'){
551                         as.destroy();
552                     }
553                 }
554             }
555         },
556
557         // inpired by a similar function in mootools library
558         /**
559          * Returns the type of object that is passed in. If the object passed in is null or undefined it
560          * return false otherwise it returns one of the following values:<ul>
561          * <li><b>string</b>: If the object passed is a string</li>
562          * <li><b>number</b>: If the object passed is a number</li>
563          * <li><b>boolean</b>: If the object passed is a boolean value</li>
564          * <li><b>function</b>: If the object passed is a function reference</li>
565          * <li><b>object</b>: If the object passed is an object</li>
566          * <li><b>array</b>: If the object passed is an array</li>
567          * <li><b>regexp</b>: If the object passed is a regular expression</li>
568          * <li><b>element</b>: If the object passed is a DOM Element</li>
569          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
570          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
571          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
572          * @param {Mixed} object
573          * @return {String}
574          */
575         type : function(o){
576             if(o === undefined || o === null){
577                 return false;
578             }
579             if(o.htmlElement){
580                 return 'element';
581             }
582             var t = typeof o;
583             if(t == 'object' && o.nodeName) {
584                 switch(o.nodeType) {
585                     case 1: return 'element';
586                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
587                 }
588             }
589             if(t == 'object' || t == 'function') {
590                 switch(o.constructor) {
591                     case Array: return 'array';
592                     case RegExp: return 'regexp';
593                 }
594                 if(typeof o.length == 'number' && typeof o.item == 'function') {
595                     return 'nodelist';
596                 }
597             }
598             return t;
599         },
600
601         /**
602          * Returns true if the passed value is null, undefined or an empty string (optional).
603          * @param {Mixed} value The value to test
604          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
605          * @return {Boolean}
606          */
607         isEmpty : function(v, allowBlank){
608             return v === null || v === undefined || (!allowBlank ? v === '' : false);
609         },
610         
611         /** @type Boolean */
612         isOpera : isOpera,
613         /** @type Boolean */
614         isSafari : isSafari,
615         /** @type Boolean */
616         isFirefox : isFirefox,
617         /** @type Boolean */
618         isIE : isIE,
619         /** @type Boolean */
620         isIE7 : isIE7,
621         /** @type Boolean */
622         isIE11 : isIE11,
623         /** @type Boolean */
624         isGecko : isGecko,
625         /** @type Boolean */
626         isBorderBox : isBorderBox,
627         /** @type Boolean */
628         isWindows : isWindows,
629         /** @type Boolean */
630         isLinux : isLinux,
631         /** @type Boolean */
632         isMac : isMac,
633         /** @type Boolean */
634         isIOS : isIOS,
635         /** @type Boolean */
636         isTouch : isTouch,
637
638         /**
639          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
640          * you may want to set this to true.
641          * @type Boolean
642          */
643         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
644         
645         
646                 
647         /**
648          * Selects a single element as a Roo Element
649          * This is about as close as you can get to jQuery's $('do crazy stuff')
650          * @param {String} selector The selector/xpath query
651          * @param {Node} root (optional) The start of the query (defaults to document).
652          * @return {Roo.Element}
653          */
654         selectNode : function(selector, root) 
655         {
656             var node = Roo.DomQuery.selectNode(selector,root);
657             return node ? Roo.get(node) : new Roo.Element(false);
658         }
659         
660     });
661
662
663 })();
664
665 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
666                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
667                 "Roo.app", "Roo.ux",
668                 "Roo.bootstrap",
669                 "Roo.bootstrap.dash");
670 /*
671  * Based on:
672  * Ext JS Library 1.1.1
673  * Copyright(c) 2006-2007, Ext JS, LLC.
674  *
675  * Originally Released Under LGPL - original licence link has changed is not relivant.
676  *
677  * Fork - LGPL
678  * <script type="text/javascript">
679  */
680
681 (function() {    
682     // wrappedn so fnCleanup is not in global scope...
683     if(Roo.isIE) {
684         function fnCleanUp() {
685             var p = Function.prototype;
686             delete p.createSequence;
687             delete p.defer;
688             delete p.createDelegate;
689             delete p.createCallback;
690             delete p.createInterceptor;
691
692             window.detachEvent("onunload", fnCleanUp);
693         }
694         window.attachEvent("onunload", fnCleanUp);
695     }
696 })();
697
698
699 /**
700  * @class Function
701  * These functions are available on every Function object (any JavaScript function).
702  */
703 Roo.apply(Function.prototype, {
704      /**
705      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
706      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
707      * Will create a function that is bound to those 2 args.
708      * @return {Function} The new function
709     */
710     createCallback : function(/*args...*/){
711         // make args available, in function below
712         var args = arguments;
713         var method = this;
714         return function() {
715             return method.apply(window, args);
716         };
717     },
718
719     /**
720      * Creates a delegate (callback) that sets the scope to obj.
721      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
722      * Will create a function that is automatically scoped to this.
723      * @param {Object} obj (optional) The object for which the scope is set
724      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
725      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
726      *                                             if a number the args are inserted at the specified position
727      * @return {Function} The new function
728      */
729     createDelegate : function(obj, args, appendArgs){
730         var method = this;
731         return function() {
732             var callArgs = args || arguments;
733             if(appendArgs === true){
734                 callArgs = Array.prototype.slice.call(arguments, 0);
735                 callArgs = callArgs.concat(args);
736             }else if(typeof appendArgs == "number"){
737                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
738                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
739                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
740             }
741             return method.apply(obj || window, callArgs);
742         };
743     },
744
745     /**
746      * Calls this function after the number of millseconds specified.
747      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
748      * @param {Object} obj (optional) The object for which the scope is set
749      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
750      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
751      *                                             if a number the args are inserted at the specified position
752      * @return {Number} The timeout id that can be used with clearTimeout
753      */
754     defer : function(millis, obj, args, appendArgs){
755         var fn = this.createDelegate(obj, args, appendArgs);
756         if(millis){
757             return setTimeout(fn, millis);
758         }
759         fn();
760         return 0;
761     },
762     /**
763      * Create a combined function call sequence of the original function + the passed function.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function
766      * @param {Function} fcn The function to sequence
767      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
768      * @return {Function} The new function
769      */
770     createSequence : function(fcn, scope){
771         if(typeof fcn != "function"){
772             return this;
773         }
774         var method = this;
775         return function() {
776             var retval = method.apply(this || window, arguments);
777             fcn.apply(scope || this || window, arguments);
778             return retval;
779         };
780     },
781
782     /**
783      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
784      * The resulting function returns the results of the original function.
785      * The passed fcn is called with the parameters of the original function.
786      * @addon
787      * @param {Function} fcn The function to call before the original
788      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
789      * @return {Function} The new function
790      */
791     createInterceptor : function(fcn, scope){
792         if(typeof fcn != "function"){
793             return this;
794         }
795         var method = this;
796         return function() {
797             fcn.target = this;
798             fcn.method = method;
799             if(fcn.apply(scope || this || window, arguments) === false){
800                 return;
801             }
802             return method.apply(this || window, arguments);
803         };
804     }
805 });
806 /*
807  * Based on:
808  * Ext JS Library 1.1.1
809  * Copyright(c) 2006-2007, Ext JS, LLC.
810  *
811  * Originally Released Under LGPL - original licence link has changed is not relivant.
812  *
813  * Fork - LGPL
814  * <script type="text/javascript">
815  */
816
817 Roo.applyIf(String, {
818     
819     /** @scope String */
820     
821     /**
822      * Escapes the passed string for ' and \
823      * @param {String} string The string to escape
824      * @return {String} The escaped string
825      * @static
826      */
827     escape : function(string) {
828         return string.replace(/('|\\)/g, "\\$1");
829     },
830
831     /**
832      * Pads the left side of a string with a specified character.  This is especially useful
833      * for normalizing number and date strings.  Example usage:
834      * <pre><code>
835 var s = String.leftPad('123', 5, '0');
836 // s now contains the string: '00123'
837 </code></pre>
838      * @param {String} string The original string
839      * @param {Number} size The total length of the output string
840      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
841      * @return {String} The padded string
842      * @static
843      */
844     leftPad : function (val, size, ch) {
845         var result = new String(val);
846         if(ch === null || ch === undefined || ch === '') {
847             ch = " ";
848         }
849         while (result.length < size) {
850             result = ch + result;
851         }
852         return result;
853     },
854
855     /**
856      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
857      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
858      * <pre><code>
859 var cls = 'my-class', text = 'Some text';
860 var s = String.format('<div class="{0}">{1}</div>', cls, text);
861 // s now contains the string: '<div class="my-class">Some text</div>'
862 </code></pre>
863      * @param {String} string The tokenized string to be formatted
864      * @param {String} value1 The value to replace token {0}
865      * @param {String} value2 Etc...
866      * @return {String} The formatted string
867      * @static
868      */
869     format : function(format){
870         var args = Array.prototype.slice.call(arguments, 1);
871         return format.replace(/\{(\d+)\}/g, function(m, i){
872             return Roo.util.Format.htmlEncode(args[i]);
873         });
874     }
875 });
876
877 /**
878  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
879  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
880  * they are already different, the first value passed in is returned.  Note that this method returns the new value
881  * but does not change the current string.
882  * <pre><code>
883 // alternate sort directions
884 sort = sort.toggle('ASC', 'DESC');
885
886 // instead of conditional logic:
887 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
888 </code></pre>
889  * @param {String} value The value to compare to the current string
890  * @param {String} other The new value to use if the string already equals the first value passed in
891  * @return {String} The new value
892  */
893  
894 String.prototype.toggle = function(value, other){
895     return this == value ? other : value;
896 };/*
897  * Based on:
898  * Ext JS Library 1.1.1
899  * Copyright(c) 2006-2007, Ext JS, LLC.
900  *
901  * Originally Released Under LGPL - original licence link has changed is not relivant.
902  *
903  * Fork - LGPL
904  * <script type="text/javascript">
905  */
906
907  /**
908  * @class Number
909  */
910 Roo.applyIf(Number.prototype, {
911     /**
912      * Checks whether or not the current number is within a desired range.  If the number is already within the
913      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
914      * exceeded.  Note that this method returns the constrained value but does not change the current number.
915      * @param {Number} min The minimum number in the range
916      * @param {Number} max The maximum number in the range
917      * @return {Number} The constrained value if outside the range, otherwise the current value
918      */
919     constrain : function(min, max){
920         return Math.min(Math.max(this, min), max);
921     }
922 });/*
923  * Based on:
924  * Ext JS Library 1.1.1
925  * Copyright(c) 2006-2007, Ext JS, LLC.
926  *
927  * Originally Released Under LGPL - original licence link has changed is not relivant.
928  *
929  * Fork - LGPL
930  * <script type="text/javascript">
931  */
932  /**
933  * @class Array
934  */
935 Roo.applyIf(Array.prototype, {
936     /**
937      * 
938      * Checks whether or not the specified object exists in the array.
939      * @param {Object} o The object to check for
940      * @return {Number} The index of o in the array (or -1 if it is not found)
941      */
942     indexOf : function(o){
943        for (var i = 0, len = this.length; i < len; i++){
944               if(this[i] == o) return i;
945        }
946            return -1;
947     },
948
949     /**
950      * Removes the specified object from the array.  If the object is not found nothing happens.
951      * @param {Object} o The object to remove
952      */
953     remove : function(o){
954        var index = this.indexOf(o);
955        if(index != -1){
956            this.splice(index, 1);
957        }
958     },
959     /**
960      * Map (JS 1.6 compatibility)
961      * @param {Function} function  to call
962      */
963     map : function(fun )
964     {
965         var len = this.length >>> 0;
966         if (typeof fun != "function")
967             throw new TypeError();
968
969         var res = new Array(len);
970         var thisp = arguments[1];
971         for (var i = 0; i < len; i++)
972         {
973             if (i in this)
974                 res[i] = fun.call(thisp, this[i], i, this);
975         }
976
977         return res;
978     }
979     
980 });
981
982
983  /*
984  * Based on:
985  * Ext JS Library 1.1.1
986  * Copyright(c) 2006-2007, Ext JS, LLC.
987  *
988  * Originally Released Under LGPL - original licence link has changed is not relivant.
989  *
990  * Fork - LGPL
991  * <script type="text/javascript">
992  */
993
994 /**
995  * @class Date
996  *
997  * The date parsing and format syntax is a subset of
998  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
999  * supported will provide results equivalent to their PHP versions.
1000  *
1001  * Following is the list of all currently supported formats:
1002  *<pre>
1003 Sample date:
1004 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1005
1006 Format  Output      Description
1007 ------  ----------  --------------------------------------------------------------
1008   d      10         Day of the month, 2 digits with leading zeros
1009   D      Wed        A textual representation of a day, three letters
1010   j      10         Day of the month without leading zeros
1011   l      Wednesday  A full textual representation of the day of the week
1012   S      th         English ordinal day of month suffix, 2 chars (use with j)
1013   w      3          Numeric representation of the day of the week
1014   z      9          The julian date, or day of the year (0-365)
1015   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1016   F      January    A full textual representation of the month
1017   m      01         Numeric representation of a month, with leading zeros
1018   M      Jan        Month name abbreviation, three letters
1019   n      1          Numeric representation of a month, without leading zeros
1020   t      31         Number of days in the given month
1021   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1022   Y      2007       A full numeric representation of a year, 4 digits
1023   y      07         A two digit representation of a year
1024   a      pm         Lowercase Ante meridiem and Post meridiem
1025   A      PM         Uppercase Ante meridiem and Post meridiem
1026   g      3          12-hour format of an hour without leading zeros
1027   G      15         24-hour format of an hour without leading zeros
1028   h      03         12-hour format of an hour with leading zeros
1029   H      15         24-hour format of an hour with leading zeros
1030   i      05         Minutes with leading zeros
1031   s      01         Seconds, with leading zeros
1032   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1033   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1034   T      CST        Timezone setting of the machine running the code
1035   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1036 </pre>
1037  *
1038  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1039  * <pre><code>
1040 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1041 document.write(dt.format('Y-m-d'));                         //2007-01-10
1042 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1043 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1044  </code></pre>
1045  *
1046  * Here are some standard date/time patterns that you might find helpful.  They
1047  * are not part of the source of Date.js, but to use them you can simply copy this
1048  * block of code into any script that is included after Date.js and they will also become
1049  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1050  * <pre><code>
1051 Date.patterns = {
1052     ISO8601Long:"Y-m-d H:i:s",
1053     ISO8601Short:"Y-m-d",
1054     ShortDate: "n/j/Y",
1055     LongDate: "l, F d, Y",
1056     FullDateTime: "l, F d, Y g:i:s A",
1057     MonthDay: "F d",
1058     ShortTime: "g:i A",
1059     LongTime: "g:i:s A",
1060     SortableDateTime: "Y-m-d\\TH:i:s",
1061     UniversalSortableDateTime: "Y-m-d H:i:sO",
1062     YearMonth: "F, Y"
1063 };
1064 </code></pre>
1065  *
1066  * Example usage:
1067  * <pre><code>
1068 var dt = new Date();
1069 document.write(dt.format(Date.patterns.ShortDate));
1070  </code></pre>
1071  */
1072
1073 /*
1074  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1075  * They generate precompiled functions from date formats instead of parsing and
1076  * processing the pattern every time you format a date.  These functions are available
1077  * on every Date object (any javascript function).
1078  *
1079  * The original article and download are here:
1080  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1081  *
1082  */
1083  
1084  
1085  // was in core
1086 /**
1087  Returns the number of milliseconds between this date and date
1088  @param {Date} date (optional) Defaults to now
1089  @return {Number} The diff in milliseconds
1090  @member Date getElapsed
1091  */
1092 Date.prototype.getElapsed = function(date) {
1093         return Math.abs((date || new Date()).getTime()-this.getTime());
1094 };
1095 // was in date file..
1096
1097
1098 // private
1099 Date.parseFunctions = {count:0};
1100 // private
1101 Date.parseRegexes = [];
1102 // private
1103 Date.formatFunctions = {count:0};
1104
1105 // private
1106 Date.prototype.dateFormat = function(format) {
1107     if (Date.formatFunctions[format] == null) {
1108         Date.createNewFormat(format);
1109     }
1110     var func = Date.formatFunctions[format];
1111     return this[func]();
1112 };
1113
1114
1115 /**
1116  * Formats a date given the supplied format string
1117  * @param {String} format The format string
1118  * @return {String} The formatted date
1119  * @method
1120  */
1121 Date.prototype.format = Date.prototype.dateFormat;
1122
1123 // private
1124 Date.createNewFormat = function(format) {
1125     var funcName = "format" + Date.formatFunctions.count++;
1126     Date.formatFunctions[format] = funcName;
1127     var code = "Date.prototype." + funcName + " = function(){return ";
1128     var special = false;
1129     var ch = '';
1130     for (var i = 0; i < format.length; ++i) {
1131         ch = format.charAt(i);
1132         if (!special && ch == "\\") {
1133             special = true;
1134         }
1135         else if (special) {
1136             special = false;
1137             code += "'" + String.escape(ch) + "' + ";
1138         }
1139         else {
1140             code += Date.getFormatCode(ch);
1141         }
1142     }
1143     /** eval:var:zzzzzzzzzzzzz */
1144     eval(code.substring(0, code.length - 3) + ";}");
1145 };
1146
1147 // private
1148 Date.getFormatCode = function(character) {
1149     switch (character) {
1150     case "d":
1151         return "String.leftPad(this.getDate(), 2, '0') + ";
1152     case "D":
1153         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1154     case "j":
1155         return "this.getDate() + ";
1156     case "l":
1157         return "Date.dayNames[this.getDay()] + ";
1158     case "S":
1159         return "this.getSuffix() + ";
1160     case "w":
1161         return "this.getDay() + ";
1162     case "z":
1163         return "this.getDayOfYear() + ";
1164     case "W":
1165         return "this.getWeekOfYear() + ";
1166     case "F":
1167         return "Date.monthNames[this.getMonth()] + ";
1168     case "m":
1169         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1170     case "M":
1171         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1172     case "n":
1173         return "(this.getMonth() + 1) + ";
1174     case "t":
1175         return "this.getDaysInMonth() + ";
1176     case "L":
1177         return "(this.isLeapYear() ? 1 : 0) + ";
1178     case "Y":
1179         return "this.getFullYear() + ";
1180     case "y":
1181         return "('' + this.getFullYear()).substring(2, 4) + ";
1182     case "a":
1183         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1184     case "A":
1185         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1186     case "g":
1187         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1188     case "G":
1189         return "this.getHours() + ";
1190     case "h":
1191         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1192     case "H":
1193         return "String.leftPad(this.getHours(), 2, '0') + ";
1194     case "i":
1195         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1196     case "s":
1197         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1198     case "O":
1199         return "this.getGMTOffset() + ";
1200     case "P":
1201         return "this.getGMTColonOffset() + ";
1202     case "T":
1203         return "this.getTimezone() + ";
1204     case "Z":
1205         return "(this.getTimezoneOffset() * -60) + ";
1206     default:
1207         return "'" + String.escape(character) + "' + ";
1208     }
1209 };
1210
1211 /**
1212  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1213  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1214  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1215  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1216  * string or the parse operation will fail.
1217  * Example Usage:
1218 <pre><code>
1219 //dt = Fri May 25 2007 (current date)
1220 var dt = new Date();
1221
1222 //dt = Thu May 25 2006 (today's month/day in 2006)
1223 dt = Date.parseDate("2006", "Y");
1224
1225 //dt = Sun Jan 15 2006 (all date parts specified)
1226 dt = Date.parseDate("2006-1-15", "Y-m-d");
1227
1228 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1229 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1230 </code></pre>
1231  * @param {String} input The unparsed date as a string
1232  * @param {String} format The format the date is in
1233  * @return {Date} The parsed date
1234  * @static
1235  */
1236 Date.parseDate = function(input, format) {
1237     if (Date.parseFunctions[format] == null) {
1238         Date.createParser(format);
1239     }
1240     var func = Date.parseFunctions[format];
1241     return Date[func](input);
1242 };
1243 /**
1244  * @private
1245  */
1246
1247 Date.createParser = function(format) {
1248     var funcName = "parse" + Date.parseFunctions.count++;
1249     var regexNum = Date.parseRegexes.length;
1250     var currentGroup = 1;
1251     Date.parseFunctions[format] = funcName;
1252
1253     var code = "Date." + funcName + " = function(input){\n"
1254         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1255         + "var d = new Date();\n"
1256         + "y = d.getFullYear();\n"
1257         + "m = d.getMonth();\n"
1258         + "d = d.getDate();\n"
1259         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1260         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1261         + "if (results && results.length > 0) {";
1262     var regex = "";
1263
1264     var special = false;
1265     var ch = '';
1266     for (var i = 0; i < format.length; ++i) {
1267         ch = format.charAt(i);
1268         if (!special && ch == "\\") {
1269             special = true;
1270         }
1271         else if (special) {
1272             special = false;
1273             regex += String.escape(ch);
1274         }
1275         else {
1276             var obj = Date.formatCodeToRegex(ch, currentGroup);
1277             currentGroup += obj.g;
1278             regex += obj.s;
1279             if (obj.g && obj.c) {
1280                 code += obj.c;
1281             }
1282         }
1283     }
1284
1285     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1286         + "{v = new Date(y, m, d, h, i, s);}\n"
1287         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1288         + "{v = new Date(y, m, d, h, i);}\n"
1289         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1290         + "{v = new Date(y, m, d, h);}\n"
1291         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1292         + "{v = new Date(y, m, d);}\n"
1293         + "else if (y >= 0 && m >= 0)\n"
1294         + "{v = new Date(y, m);}\n"
1295         + "else if (y >= 0)\n"
1296         + "{v = new Date(y);}\n"
1297         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1298         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1299         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1300         + ";}";
1301
1302     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1303     /** eval:var:zzzzzzzzzzzzz */
1304     eval(code);
1305 };
1306
1307 // private
1308 Date.formatCodeToRegex = function(character, currentGroup) {
1309     switch (character) {
1310     case "D":
1311         return {g:0,
1312         c:null,
1313         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1314     case "j":
1315         return {g:1,
1316             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1317             s:"(\\d{1,2})"}; // day of month without leading zeroes
1318     case "d":
1319         return {g:1,
1320             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1321             s:"(\\d{2})"}; // day of month with leading zeroes
1322     case "l":
1323         return {g:0,
1324             c:null,
1325             s:"(?:" + Date.dayNames.join("|") + ")"};
1326     case "S":
1327         return {g:0,
1328             c:null,
1329             s:"(?:st|nd|rd|th)"};
1330     case "w":
1331         return {g:0,
1332             c:null,
1333             s:"\\d"};
1334     case "z":
1335         return {g:0,
1336             c:null,
1337             s:"(?:\\d{1,3})"};
1338     case "W":
1339         return {g:0,
1340             c:null,
1341             s:"(?:\\d{2})"};
1342     case "F":
1343         return {g:1,
1344             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1345             s:"(" + Date.monthNames.join("|") + ")"};
1346     case "M":
1347         return {g:1,
1348             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1349             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1350     case "n":
1351         return {g:1,
1352             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1353             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1354     case "m":
1355         return {g:1,
1356             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1357             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1358     case "t":
1359         return {g:0,
1360             c:null,
1361             s:"\\d{1,2}"};
1362     case "L":
1363         return {g:0,
1364             c:null,
1365             s:"(?:1|0)"};
1366     case "Y":
1367         return {g:1,
1368             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1369             s:"(\\d{4})"};
1370     case "y":
1371         return {g:1,
1372             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1373                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1374             s:"(\\d{1,2})"};
1375     case "a":
1376         return {g:1,
1377             c:"if (results[" + currentGroup + "] == 'am') {\n"
1378                 + "if (h == 12) { h = 0; }\n"
1379                 + "} else { if (h < 12) { h += 12; }}",
1380             s:"(am|pm)"};
1381     case "A":
1382         return {g:1,
1383             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1384                 + "if (h == 12) { h = 0; }\n"
1385                 + "} else { if (h < 12) { h += 12; }}",
1386             s:"(AM|PM)"};
1387     case "g":
1388     case "G":
1389         return {g:1,
1390             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1391             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1392     case "h":
1393     case "H":
1394         return {g:1,
1395             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1396             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1397     case "i":
1398         return {g:1,
1399             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1400             s:"(\\d{2})"};
1401     case "s":
1402         return {g:1,
1403             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1404             s:"(\\d{2})"};
1405     case "O":
1406         return {g:1,
1407             c:[
1408                 "o = results[", currentGroup, "];\n",
1409                 "var sn = o.substring(0,1);\n", // get + / - sign
1410                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1411                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1412                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1413                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1414             ].join(""),
1415             s:"([+\-]\\d{2,4})"};
1416     
1417     
1418     case "P":
1419         return {g:1,
1420                 c:[
1421                    "o = results[", currentGroup, "];\n",
1422                    "var sn = o.substring(0,1);\n",
1423                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1424                    "var mn = o.substring(4,6) % 60;\n",
1425                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1426                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1427             ].join(""),
1428             s:"([+\-]\\d{4})"};
1429     case "T":
1430         return {g:0,
1431             c:null,
1432             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1433     case "Z":
1434         return {g:1,
1435             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1436                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1437             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1438     default:
1439         return {g:0,
1440             c:null,
1441             s:String.escape(character)};
1442     }
1443 };
1444
1445 /**
1446  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1447  * @return {String} The abbreviated timezone name (e.g. 'CST')
1448  */
1449 Date.prototype.getTimezone = function() {
1450     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1451 };
1452
1453 /**
1454  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1455  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1456  */
1457 Date.prototype.getGMTOffset = function() {
1458     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1459         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1460         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1461 };
1462
1463 /**
1464  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1465  * @return {String} 2-characters representing hours and 2-characters representing minutes
1466  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1467  */
1468 Date.prototype.getGMTColonOffset = function() {
1469         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1470                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1471                 + ":"
1472                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1473 }
1474
1475 /**
1476  * Get the numeric day number of the year, adjusted for leap year.
1477  * @return {Number} 0 through 364 (365 in leap years)
1478  */
1479 Date.prototype.getDayOfYear = function() {
1480     var num = 0;
1481     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1482     for (var i = 0; i < this.getMonth(); ++i) {
1483         num += Date.daysInMonth[i];
1484     }
1485     return num + this.getDate() - 1;
1486 };
1487
1488 /**
1489  * Get the string representation of the numeric week number of the year
1490  * (equivalent to the format specifier 'W').
1491  * @return {String} '00' through '52'
1492  */
1493 Date.prototype.getWeekOfYear = function() {
1494     // Skip to Thursday of this week
1495     var now = this.getDayOfYear() + (4 - this.getDay());
1496     // Find the first Thursday of the year
1497     var jan1 = new Date(this.getFullYear(), 0, 1);
1498     var then = (7 - jan1.getDay() + 4);
1499     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1500 };
1501
1502 /**
1503  * Whether or not the current date is in a leap year.
1504  * @return {Boolean} True if the current date is in a leap year, else false
1505  */
1506 Date.prototype.isLeapYear = function() {
1507     var year = this.getFullYear();
1508     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1509 };
1510
1511 /**
1512  * Get the first day of the current month, adjusted for leap year.  The returned value
1513  * is the numeric day index within the week (0-6) which can be used in conjunction with
1514  * the {@link #monthNames} array to retrieve the textual day name.
1515  * Example:
1516  *<pre><code>
1517 var dt = new Date('1/10/2007');
1518 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1519 </code></pre>
1520  * @return {Number} The day number (0-6)
1521  */
1522 Date.prototype.getFirstDayOfMonth = function() {
1523     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1524     return (day < 0) ? (day + 7) : day;
1525 };
1526
1527 /**
1528  * Get the last day of the current month, adjusted for leap year.  The returned value
1529  * is the numeric day index within the week (0-6) which can be used in conjunction with
1530  * the {@link #monthNames} array to retrieve the textual day name.
1531  * Example:
1532  *<pre><code>
1533 var dt = new Date('1/10/2007');
1534 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1535 </code></pre>
1536  * @return {Number} The day number (0-6)
1537  */
1538 Date.prototype.getLastDayOfMonth = function() {
1539     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1540     return (day < 0) ? (day + 7) : day;
1541 };
1542
1543
1544 /**
1545  * Get the first date of this date's month
1546  * @return {Date}
1547  */
1548 Date.prototype.getFirstDateOfMonth = function() {
1549     return new Date(this.getFullYear(), this.getMonth(), 1);
1550 };
1551
1552 /**
1553  * Get the last date of this date's month
1554  * @return {Date}
1555  */
1556 Date.prototype.getLastDateOfMonth = function() {
1557     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1558 };
1559 /**
1560  * Get the number of days in the current month, adjusted for leap year.
1561  * @return {Number} The number of days in the month
1562  */
1563 Date.prototype.getDaysInMonth = function() {
1564     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1565     return Date.daysInMonth[this.getMonth()];
1566 };
1567
1568 /**
1569  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1570  * @return {String} 'st, 'nd', 'rd' or 'th'
1571  */
1572 Date.prototype.getSuffix = function() {
1573     switch (this.getDate()) {
1574         case 1:
1575         case 21:
1576         case 31:
1577             return "st";
1578         case 2:
1579         case 22:
1580             return "nd";
1581         case 3:
1582         case 23:
1583             return "rd";
1584         default:
1585             return "th";
1586     }
1587 };
1588
1589 // private
1590 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1591
1592 /**
1593  * An array of textual month names.
1594  * Override these values for international dates, for example...
1595  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1596  * @type Array
1597  * @static
1598  */
1599 Date.monthNames =
1600    ["January",
1601     "February",
1602     "March",
1603     "April",
1604     "May",
1605     "June",
1606     "July",
1607     "August",
1608     "September",
1609     "October",
1610     "November",
1611     "December"];
1612
1613 /**
1614  * An array of textual day names.
1615  * Override these values for international dates, for example...
1616  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1617  * @type Array
1618  * @static
1619  */
1620 Date.dayNames =
1621    ["Sunday",
1622     "Monday",
1623     "Tuesday",
1624     "Wednesday",
1625     "Thursday",
1626     "Friday",
1627     "Saturday"];
1628
1629 // private
1630 Date.y2kYear = 50;
1631 // private
1632 Date.monthNumbers = {
1633     Jan:0,
1634     Feb:1,
1635     Mar:2,
1636     Apr:3,
1637     May:4,
1638     Jun:5,
1639     Jul:6,
1640     Aug:7,
1641     Sep:8,
1642     Oct:9,
1643     Nov:10,
1644     Dec:11};
1645
1646 /**
1647  * Creates and returns a new Date instance with the exact same date value as the called instance.
1648  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1649  * variable will also be changed.  When the intention is to create a new variable that will not
1650  * modify the original instance, you should create a clone.
1651  *
1652  * Example of correctly cloning a date:
1653  * <pre><code>
1654 //wrong way:
1655 var orig = new Date('10/1/2006');
1656 var copy = orig;
1657 copy.setDate(5);
1658 document.write(orig);  //returns 'Thu Oct 05 2006'!
1659
1660 //correct way:
1661 var orig = new Date('10/1/2006');
1662 var copy = orig.clone();
1663 copy.setDate(5);
1664 document.write(orig);  //returns 'Thu Oct 01 2006'
1665 </code></pre>
1666  * @return {Date} The new Date instance
1667  */
1668 Date.prototype.clone = function() {
1669         return new Date(this.getTime());
1670 };
1671
1672 /**
1673  * Clears any time information from this date
1674  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1675  @return {Date} this or the clone
1676  */
1677 Date.prototype.clearTime = function(clone){
1678     if(clone){
1679         return this.clone().clearTime();
1680     }
1681     this.setHours(0);
1682     this.setMinutes(0);
1683     this.setSeconds(0);
1684     this.setMilliseconds(0);
1685     return this;
1686 };
1687
1688 // private
1689 // safari setMonth is broken
1690 if(Roo.isSafari){
1691     Date.brokenSetMonth = Date.prototype.setMonth;
1692         Date.prototype.setMonth = function(num){
1693                 if(num <= -1){
1694                         var n = Math.ceil(-num);
1695                         var back_year = Math.ceil(n/12);
1696                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1697                         this.setFullYear(this.getFullYear() - back_year);
1698                         return Date.brokenSetMonth.call(this, month);
1699                 } else {
1700                         return Date.brokenSetMonth.apply(this, arguments);
1701                 }
1702         };
1703 }
1704
1705 /** Date interval constant 
1706 * @static 
1707 * @type String */
1708 Date.MILLI = "ms";
1709 /** Date interval constant 
1710 * @static 
1711 * @type String */
1712 Date.SECOND = "s";
1713 /** Date interval constant 
1714 * @static 
1715 * @type String */
1716 Date.MINUTE = "mi";
1717 /** Date interval constant 
1718 * @static 
1719 * @type String */
1720 Date.HOUR = "h";
1721 /** Date interval constant 
1722 * @static 
1723 * @type String */
1724 Date.DAY = "d";
1725 /** Date interval constant 
1726 * @static 
1727 * @type String */
1728 Date.MONTH = "mo";
1729 /** Date interval constant 
1730 * @static 
1731 * @type String */
1732 Date.YEAR = "y";
1733
1734 /**
1735  * Provides a convenient method of performing basic date arithmetic.  This method
1736  * does not modify the Date instance being called - it creates and returns
1737  * a new Date instance containing the resulting date value.
1738  *
1739  * Examples:
1740  * <pre><code>
1741 //Basic usage:
1742 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1743 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1744
1745 //Negative values will subtract correctly:
1746 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1747 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1748
1749 //You can even chain several calls together in one line!
1750 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1751 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1752  </code></pre>
1753  *
1754  * @param {String} interval   A valid date interval enum value
1755  * @param {Number} value      The amount to add to the current date
1756  * @return {Date} The new Date instance
1757  */
1758 Date.prototype.add = function(interval, value){
1759   var d = this.clone();
1760   if (!interval || value === 0) return d;
1761   switch(interval.toLowerCase()){
1762     case Date.MILLI:
1763       d.setMilliseconds(this.getMilliseconds() + value);
1764       break;
1765     case Date.SECOND:
1766       d.setSeconds(this.getSeconds() + value);
1767       break;
1768     case Date.MINUTE:
1769       d.setMinutes(this.getMinutes() + value);
1770       break;
1771     case Date.HOUR:
1772       d.setHours(this.getHours() + value);
1773       break;
1774     case Date.DAY:
1775       d.setDate(this.getDate() + value);
1776       break;
1777     case Date.MONTH:
1778       var day = this.getDate();
1779       if(day > 28){
1780           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1781       }
1782       d.setDate(day);
1783       d.setMonth(this.getMonth() + value);
1784       break;
1785     case Date.YEAR:
1786       d.setFullYear(this.getFullYear() + value);
1787       break;
1788   }
1789   return d;
1790 };
1791 /*
1792  * Based on:
1793  * Ext JS Library 1.1.1
1794  * Copyright(c) 2006-2007, Ext JS, LLC.
1795  *
1796  * Originally Released Under LGPL - original licence link has changed is not relivant.
1797  *
1798  * Fork - LGPL
1799  * <script type="text/javascript">
1800  */
1801
1802 /**
1803  * @class Roo.lib.Dom
1804  * @static
1805  * 
1806  * Dom utils (from YIU afaik)
1807  * 
1808  **/
1809 Roo.lib.Dom = {
1810     /**
1811      * Get the view width
1812      * @param {Boolean} full True will get the full document, otherwise it's the view width
1813      * @return {Number} The width
1814      */
1815      
1816     getViewWidth : function(full) {
1817         return full ? this.getDocumentWidth() : this.getViewportWidth();
1818     },
1819     /**
1820      * Get the view height
1821      * @param {Boolean} full True will get the full document, otherwise it's the view height
1822      * @return {Number} The height
1823      */
1824     getViewHeight : function(full) {
1825         return full ? this.getDocumentHeight() : this.getViewportHeight();
1826     },
1827
1828     getDocumentHeight: function() {
1829         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1830         return Math.max(scrollHeight, this.getViewportHeight());
1831     },
1832
1833     getDocumentWidth: function() {
1834         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1835         return Math.max(scrollWidth, this.getViewportWidth());
1836     },
1837
1838     getViewportHeight: function() {
1839         var height = self.innerHeight;
1840         var mode = document.compatMode;
1841
1842         if ((mode || Roo.isIE) && !Roo.isOpera) {
1843             height = (mode == "CSS1Compat") ?
1844                      document.documentElement.clientHeight :
1845                      document.body.clientHeight;
1846         }
1847
1848         return height;
1849     },
1850
1851     getViewportWidth: function() {
1852         var width = self.innerWidth;
1853         var mode = document.compatMode;
1854
1855         if (mode || Roo.isIE) {
1856             width = (mode == "CSS1Compat") ?
1857                     document.documentElement.clientWidth :
1858                     document.body.clientWidth;
1859         }
1860         return width;
1861     },
1862
1863     isAncestor : function(p, c) {
1864         p = Roo.getDom(p);
1865         c = Roo.getDom(c);
1866         if (!p || !c) {
1867             return false;
1868         }
1869
1870         if (p.contains && !Roo.isSafari) {
1871             return p.contains(c);
1872         } else if (p.compareDocumentPosition) {
1873             return !!(p.compareDocumentPosition(c) & 16);
1874         } else {
1875             var parent = c.parentNode;
1876             while (parent) {
1877                 if (parent == p) {
1878                     return true;
1879                 }
1880                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1881                     return false;
1882                 }
1883                 parent = parent.parentNode;
1884             }
1885             return false;
1886         }
1887     },
1888
1889     getRegion : function(el) {
1890         return Roo.lib.Region.getRegion(el);
1891     },
1892
1893     getY : function(el) {
1894         return this.getXY(el)[1];
1895     },
1896
1897     getX : function(el) {
1898         return this.getXY(el)[0];
1899     },
1900
1901     getXY : function(el) {
1902         var p, pe, b, scroll, bd = document.body;
1903         el = Roo.getDom(el);
1904         var fly = Roo.lib.AnimBase.fly;
1905         if (el.getBoundingClientRect) {
1906             b = el.getBoundingClientRect();
1907             scroll = fly(document).getScroll();
1908             return [b.left + scroll.left, b.top + scroll.top];
1909         }
1910         var x = 0, y = 0;
1911
1912         p = el;
1913
1914         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1915
1916         while (p) {
1917
1918             x += p.offsetLeft;
1919             y += p.offsetTop;
1920
1921             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1922                 hasAbsolute = true;
1923             }
1924
1925             if (Roo.isGecko) {
1926                 pe = fly(p);
1927
1928                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1929                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1930
1931
1932                 x += bl;
1933                 y += bt;
1934
1935
1936                 if (p != el && pe.getStyle('overflow') != 'visible') {
1937                     x += bl;
1938                     y += bt;
1939                 }
1940             }
1941             p = p.offsetParent;
1942         }
1943
1944         if (Roo.isSafari && hasAbsolute) {
1945             x -= bd.offsetLeft;
1946             y -= bd.offsetTop;
1947         }
1948
1949         if (Roo.isGecko && !hasAbsolute) {
1950             var dbd = fly(bd);
1951             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1952             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1953         }
1954
1955         p = el.parentNode;
1956         while (p && p != bd) {
1957             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1958                 x -= p.scrollLeft;
1959                 y -= p.scrollTop;
1960             }
1961             p = p.parentNode;
1962         }
1963         return [x, y];
1964     },
1965  
1966   
1967
1968
1969     setXY : function(el, xy) {
1970         el = Roo.fly(el, '_setXY');
1971         el.position();
1972         var pts = el.translatePoints(xy);
1973         if (xy[0] !== false) {
1974             el.dom.style.left = pts.left + "px";
1975         }
1976         if (xy[1] !== false) {
1977             el.dom.style.top = pts.top + "px";
1978         }
1979     },
1980
1981     setX : function(el, x) {
1982         this.setXY(el, [x, false]);
1983     },
1984
1985     setY : function(el, y) {
1986         this.setXY(el, [false, y]);
1987     }
1988 };
1989 /*
1990  * Portions of this file are based on pieces of Yahoo User Interface Library
1991  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1992  * YUI licensed under the BSD License:
1993  * http://developer.yahoo.net/yui/license.txt
1994  * <script type="text/javascript">
1995  *
1996  */
1997
1998 Roo.lib.Event = function() {
1999     var loadComplete = false;
2000     var listeners = [];
2001     var unloadListeners = [];
2002     var retryCount = 0;
2003     var onAvailStack = [];
2004     var counter = 0;
2005     var lastError = null;
2006
2007     return {
2008         POLL_RETRYS: 200,
2009         POLL_INTERVAL: 20,
2010         EL: 0,
2011         TYPE: 1,
2012         FN: 2,
2013         WFN: 3,
2014         OBJ: 3,
2015         ADJ_SCOPE: 4,
2016         _interval: null,
2017
2018         startInterval: function() {
2019             if (!this._interval) {
2020                 var self = this;
2021                 var callback = function() {
2022                     self._tryPreloadAttach();
2023                 };
2024                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2025
2026             }
2027         },
2028
2029         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2030             onAvailStack.push({ id:         p_id,
2031                 fn:         p_fn,
2032                 obj:        p_obj,
2033                 override:   p_override,
2034                 checkReady: false    });
2035
2036             retryCount = this.POLL_RETRYS;
2037             this.startInterval();
2038         },
2039
2040
2041         addListener: function(el, eventName, fn) {
2042             el = Roo.getDom(el);
2043             if (!el || !fn) {
2044                 return false;
2045             }
2046
2047             if ("unload" == eventName) {
2048                 unloadListeners[unloadListeners.length] =
2049                 [el, eventName, fn];
2050                 return true;
2051             }
2052
2053             var wrappedFn = function(e) {
2054                 return fn(Roo.lib.Event.getEvent(e));
2055             };
2056
2057             var li = [el, eventName, fn, wrappedFn];
2058
2059             var index = listeners.length;
2060             listeners[index] = li;
2061
2062             this.doAdd(el, eventName, wrappedFn, false);
2063             return true;
2064
2065         },
2066
2067
2068         removeListener: function(el, eventName, fn) {
2069             var i, len;
2070
2071             el = Roo.getDom(el);
2072
2073             if(!fn) {
2074                 return this.purgeElement(el, false, eventName);
2075             }
2076
2077
2078             if ("unload" == eventName) {
2079
2080                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2081                     var li = unloadListeners[i];
2082                     if (li &&
2083                         li[0] == el &&
2084                         li[1] == eventName &&
2085                         li[2] == fn) {
2086                         unloadListeners.splice(i, 1);
2087                         return true;
2088                     }
2089                 }
2090
2091                 return false;
2092             }
2093
2094             var cacheItem = null;
2095
2096
2097             var index = arguments[3];
2098
2099             if ("undefined" == typeof index) {
2100                 index = this._getCacheIndex(el, eventName, fn);
2101             }
2102
2103             if (index >= 0) {
2104                 cacheItem = listeners[index];
2105             }
2106
2107             if (!el || !cacheItem) {
2108                 return false;
2109             }
2110
2111             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2112
2113             delete listeners[index][this.WFN];
2114             delete listeners[index][this.FN];
2115             listeners.splice(index, 1);
2116
2117             return true;
2118
2119         },
2120
2121
2122         getTarget: function(ev, resolveTextNode) {
2123             ev = ev.browserEvent || ev;
2124             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2125             var t = ev.target || ev.srcElement;
2126             return this.resolveTextNode(t);
2127         },
2128
2129
2130         resolveTextNode: function(node) {
2131             if (Roo.isSafari && node && 3 == node.nodeType) {
2132                 return node.parentNode;
2133             } else {
2134                 return node;
2135             }
2136         },
2137
2138
2139         getPageX: function(ev) {
2140             ev = ev.browserEvent || ev;
2141             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2142             var x = ev.pageX;
2143             if (!x && 0 !== x) {
2144                 x = ev.clientX || 0;
2145
2146                 if (Roo.isIE) {
2147                     x += this.getScroll()[1];
2148                 }
2149             }
2150
2151             return x;
2152         },
2153
2154
2155         getPageY: function(ev) {
2156             ev = ev.browserEvent || ev;
2157             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2158             var y = ev.pageY;
2159             if (!y && 0 !== y) {
2160                 y = ev.clientY || 0;
2161
2162                 if (Roo.isIE) {
2163                     y += this.getScroll()[0];
2164                 }
2165             }
2166
2167
2168             return y;
2169         },
2170
2171
2172         getXY: function(ev) {
2173             ev = ev.browserEvent || ev;
2174             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2175             return [this.getPageX(ev), this.getPageY(ev)];
2176         },
2177
2178
2179         getRelatedTarget: function(ev) {
2180             ev = ev.browserEvent || ev;
2181             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2182             var t = ev.relatedTarget;
2183             if (!t) {
2184                 if (ev.type == "mouseout") {
2185                     t = ev.toElement;
2186                 } else if (ev.type == "mouseover") {
2187                     t = ev.fromElement;
2188                 }
2189             }
2190
2191             return this.resolveTextNode(t);
2192         },
2193
2194
2195         getTime: function(ev) {
2196             ev = ev.browserEvent || ev;
2197             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2198             if (!ev.time) {
2199                 var t = new Date().getTime();
2200                 try {
2201                     ev.time = t;
2202                 } catch(ex) {
2203                     this.lastError = ex;
2204                     return t;
2205                 }
2206             }
2207
2208             return ev.time;
2209         },
2210
2211
2212         stopEvent: function(ev) {
2213             this.stopPropagation(ev);
2214             this.preventDefault(ev);
2215         },
2216
2217
2218         stopPropagation: function(ev) {
2219             ev = ev.browserEvent || ev;
2220             if (ev.stopPropagation) {
2221                 ev.stopPropagation();
2222             } else {
2223                 ev.cancelBubble = true;
2224             }
2225         },
2226
2227
2228         preventDefault: function(ev) {
2229             ev = ev.browserEvent || ev;
2230             if(ev.preventDefault) {
2231                 ev.preventDefault();
2232             } else {
2233                 ev.returnValue = false;
2234             }
2235         },
2236
2237
2238         getEvent: function(e) {
2239             var ev = e || window.event;
2240             if (!ev) {
2241                 var c = this.getEvent.caller;
2242                 while (c) {
2243                     ev = c.arguments[0];
2244                     if (ev && Event == ev.constructor) {
2245                         break;
2246                     }
2247                     c = c.caller;
2248                 }
2249             }
2250             return ev;
2251         },
2252
2253
2254         getCharCode: function(ev) {
2255             ev = ev.browserEvent || ev;
2256             return ev.charCode || ev.keyCode || 0;
2257         },
2258
2259
2260         _getCacheIndex: function(el, eventName, fn) {
2261             for (var i = 0,len = listeners.length; i < len; ++i) {
2262                 var li = listeners[i];
2263                 if (li &&
2264                     li[this.FN] == fn &&
2265                     li[this.EL] == el &&
2266                     li[this.TYPE] == eventName) {
2267                     return i;
2268                 }
2269             }
2270
2271             return -1;
2272         },
2273
2274
2275         elCache: {},
2276
2277
2278         getEl: function(id) {
2279             return document.getElementById(id);
2280         },
2281
2282
2283         clearCache: function() {
2284         },
2285
2286
2287         _load: function(e) {
2288             loadComplete = true;
2289             var EU = Roo.lib.Event;
2290
2291
2292             if (Roo.isIE) {
2293                 EU.doRemove(window, "load", EU._load);
2294             }
2295         },
2296
2297
2298         _tryPreloadAttach: function() {
2299
2300             if (this.locked) {
2301                 return false;
2302             }
2303
2304             this.locked = true;
2305
2306
2307             var tryAgain = !loadComplete;
2308             if (!tryAgain) {
2309                 tryAgain = (retryCount > 0);
2310             }
2311
2312
2313             var notAvail = [];
2314             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2315                 var item = onAvailStack[i];
2316                 if (item) {
2317                     var el = this.getEl(item.id);
2318
2319                     if (el) {
2320                         if (!item.checkReady ||
2321                             loadComplete ||
2322                             el.nextSibling ||
2323                             (document && document.body)) {
2324
2325                             var scope = el;
2326                             if (item.override) {
2327                                 if (item.override === true) {
2328                                     scope = item.obj;
2329                                 } else {
2330                                     scope = item.override;
2331                                 }
2332                             }
2333                             item.fn.call(scope, item.obj);
2334                             onAvailStack[i] = null;
2335                         }
2336                     } else {
2337                         notAvail.push(item);
2338                     }
2339                 }
2340             }
2341
2342             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2343
2344             if (tryAgain) {
2345
2346                 this.startInterval();
2347             } else {
2348                 clearInterval(this._interval);
2349                 this._interval = null;
2350             }
2351
2352             this.locked = false;
2353
2354             return true;
2355
2356         },
2357
2358
2359         purgeElement: function(el, recurse, eventName) {
2360             var elListeners = this.getListeners(el, eventName);
2361             if (elListeners) {
2362                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2363                     var l = elListeners[i];
2364                     this.removeListener(el, l.type, l.fn);
2365                 }
2366             }
2367
2368             if (recurse && el && el.childNodes) {
2369                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2370                     this.purgeElement(el.childNodes[i], recurse, eventName);
2371                 }
2372             }
2373         },
2374
2375
2376         getListeners: function(el, eventName) {
2377             var results = [], searchLists;
2378             if (!eventName) {
2379                 searchLists = [listeners, unloadListeners];
2380             } else if (eventName == "unload") {
2381                 searchLists = [unloadListeners];
2382             } else {
2383                 searchLists = [listeners];
2384             }
2385
2386             for (var j = 0; j < searchLists.length; ++j) {
2387                 var searchList = searchLists[j];
2388                 if (searchList && searchList.length > 0) {
2389                     for (var i = 0,len = searchList.length; i < len; ++i) {
2390                         var l = searchList[i];
2391                         if (l && l[this.EL] === el &&
2392                             (!eventName || eventName === l[this.TYPE])) {
2393                             results.push({
2394                                 type:   l[this.TYPE],
2395                                 fn:     l[this.FN],
2396                                 obj:    l[this.OBJ],
2397                                 adjust: l[this.ADJ_SCOPE],
2398                                 index:  i
2399                             });
2400                         }
2401                     }
2402                 }
2403             }
2404
2405             return (results.length) ? results : null;
2406         },
2407
2408
2409         _unload: function(e) {
2410
2411             var EU = Roo.lib.Event, i, j, l, len, index;
2412
2413             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2414                 l = unloadListeners[i];
2415                 if (l) {
2416                     var scope = window;
2417                     if (l[EU.ADJ_SCOPE]) {
2418                         if (l[EU.ADJ_SCOPE] === true) {
2419                             scope = l[EU.OBJ];
2420                         } else {
2421                             scope = l[EU.ADJ_SCOPE];
2422                         }
2423                     }
2424                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2425                     unloadListeners[i] = null;
2426                     l = null;
2427                     scope = null;
2428                 }
2429             }
2430
2431             unloadListeners = null;
2432
2433             if (listeners && listeners.length > 0) {
2434                 j = listeners.length;
2435                 while (j) {
2436                     index = j - 1;
2437                     l = listeners[index];
2438                     if (l) {
2439                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2440                                 l[EU.FN], index);
2441                     }
2442                     j = j - 1;
2443                 }
2444                 l = null;
2445
2446                 EU.clearCache();
2447             }
2448
2449             EU.doRemove(window, "unload", EU._unload);
2450
2451         },
2452
2453
2454         getScroll: function() {
2455             var dd = document.documentElement, db = document.body;
2456             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2457                 return [dd.scrollTop, dd.scrollLeft];
2458             } else if (db) {
2459                 return [db.scrollTop, db.scrollLeft];
2460             } else {
2461                 return [0, 0];
2462             }
2463         },
2464
2465
2466         doAdd: function () {
2467             if (window.addEventListener) {
2468                 return function(el, eventName, fn, capture) {
2469                     el.addEventListener(eventName, fn, (capture));
2470                 };
2471             } else if (window.attachEvent) {
2472                 return function(el, eventName, fn, capture) {
2473                     el.attachEvent("on" + eventName, fn);
2474                 };
2475             } else {
2476                 return function() {
2477                 };
2478             }
2479         }(),
2480
2481
2482         doRemove: function() {
2483             if (window.removeEventListener) {
2484                 return function (el, eventName, fn, capture) {
2485                     el.removeEventListener(eventName, fn, (capture));
2486                 };
2487             } else if (window.detachEvent) {
2488                 return function (el, eventName, fn) {
2489                     el.detachEvent("on" + eventName, fn);
2490                 };
2491             } else {
2492                 return function() {
2493                 };
2494             }
2495         }()
2496     };
2497     
2498 }();
2499 (function() {     
2500    
2501     var E = Roo.lib.Event;
2502     E.on = E.addListener;
2503     E.un = E.removeListener;
2504
2505     if (document && document.body) {
2506         E._load();
2507     } else {
2508         E.doAdd(window, "load", E._load);
2509     }
2510     E.doAdd(window, "unload", E._unload);
2511     E._tryPreloadAttach();
2512 })();
2513
2514 /*
2515  * Portions of this file are based on pieces of Yahoo User Interface Library
2516  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2517  * YUI licensed under the BSD License:
2518  * http://developer.yahoo.net/yui/license.txt
2519  * <script type="text/javascript">
2520  *
2521  */
2522
2523 (function() {
2524     /**
2525      * @class Roo.lib.Ajax
2526      *
2527      */
2528     Roo.lib.Ajax = {
2529         /**
2530          * @static 
2531          */
2532         request : function(method, uri, cb, data, options) {
2533             if(options){
2534                 var hs = options.headers;
2535                 if(hs){
2536                     for(var h in hs){
2537                         if(hs.hasOwnProperty(h)){
2538                             this.initHeader(h, hs[h], false);
2539                         }
2540                     }
2541                 }
2542                 if(options.xmlData){
2543                     this.initHeader('Content-Type', 'text/xml', false);
2544                     method = 'POST';
2545                     data = options.xmlData;
2546                 }
2547             }
2548
2549             return this.asyncRequest(method, uri, cb, data);
2550         },
2551
2552         serializeForm : function(form) {
2553             if(typeof form == 'string') {
2554                 form = (document.getElementById(form) || document.forms[form]);
2555             }
2556
2557             var el, name, val, disabled, data = '', hasSubmit = false;
2558             for (var i = 0; i < form.elements.length; i++) {
2559                 el = form.elements[i];
2560                 disabled = form.elements[i].disabled;
2561                 name = form.elements[i].name;
2562                 val = form.elements[i].value;
2563
2564                 if (!disabled && name){
2565                     switch (el.type)
2566                             {
2567                         case 'select-one':
2568                         case 'select-multiple':
2569                             for (var j = 0; j < el.options.length; j++) {
2570                                 if (el.options[j].selected) {
2571                                     if (Roo.isIE) {
2572                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2573                                     }
2574                                     else {
2575                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2576                                     }
2577                                 }
2578                             }
2579                             break;
2580                         case 'radio':
2581                         case 'checkbox':
2582                             if (el.checked) {
2583                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2584                             }
2585                             break;
2586                         case 'file':
2587
2588                         case undefined:
2589
2590                         case 'reset':
2591
2592                         case 'button':
2593
2594                             break;
2595                         case 'submit':
2596                             if(hasSubmit == false) {
2597                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2598                                 hasSubmit = true;
2599                             }
2600                             break;
2601                         default:
2602                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2603                             break;
2604                     }
2605                 }
2606             }
2607             data = data.substr(0, data.length - 1);
2608             return data;
2609         },
2610
2611         headers:{},
2612
2613         hasHeaders:false,
2614
2615         useDefaultHeader:true,
2616
2617         defaultPostHeader:'application/x-www-form-urlencoded',
2618
2619         useDefaultXhrHeader:true,
2620
2621         defaultXhrHeader:'XMLHttpRequest',
2622
2623         hasDefaultHeaders:true,
2624
2625         defaultHeaders:{},
2626
2627         poll:{},
2628
2629         timeout:{},
2630
2631         pollInterval:50,
2632
2633         transactionId:0,
2634
2635         setProgId:function(id)
2636         {
2637             this.activeX.unshift(id);
2638         },
2639
2640         setDefaultPostHeader:function(b)
2641         {
2642             this.useDefaultHeader = b;
2643         },
2644
2645         setDefaultXhrHeader:function(b)
2646         {
2647             this.useDefaultXhrHeader = b;
2648         },
2649
2650         setPollingInterval:function(i)
2651         {
2652             if (typeof i == 'number' && isFinite(i)) {
2653                 this.pollInterval = i;
2654             }
2655         },
2656
2657         createXhrObject:function(transactionId)
2658         {
2659             var obj,http;
2660             try
2661             {
2662
2663                 http = new XMLHttpRequest();
2664
2665                 obj = { conn:http, tId:transactionId };
2666             }
2667             catch(e)
2668             {
2669                 for (var i = 0; i < this.activeX.length; ++i) {
2670                     try
2671                     {
2672
2673                         http = new ActiveXObject(this.activeX[i]);
2674
2675                         obj = { conn:http, tId:transactionId };
2676                         break;
2677                     }
2678                     catch(e) {
2679                     }
2680                 }
2681             }
2682             finally
2683             {
2684                 return obj;
2685             }
2686         },
2687
2688         getConnectionObject:function()
2689         {
2690             var o;
2691             var tId = this.transactionId;
2692
2693             try
2694             {
2695                 o = this.createXhrObject(tId);
2696                 if (o) {
2697                     this.transactionId++;
2698                 }
2699             }
2700             catch(e) {
2701             }
2702             finally
2703             {
2704                 return o;
2705             }
2706         },
2707
2708         asyncRequest:function(method, uri, callback, postData)
2709         {
2710             var o = this.getConnectionObject();
2711
2712             if (!o) {
2713                 return null;
2714             }
2715             else {
2716                 o.conn.open(method, uri, true);
2717
2718                 if (this.useDefaultXhrHeader) {
2719                     if (!this.defaultHeaders['X-Requested-With']) {
2720                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2721                     }
2722                 }
2723
2724                 if(postData && this.useDefaultHeader){
2725                     this.initHeader('Content-Type', this.defaultPostHeader);
2726                 }
2727
2728                  if (this.hasDefaultHeaders || this.hasHeaders) {
2729                     this.setHeader(o);
2730                 }
2731
2732                 this.handleReadyState(o, callback);
2733                 o.conn.send(postData || null);
2734
2735                 return o;
2736             }
2737         },
2738
2739         handleReadyState:function(o, callback)
2740         {
2741             var oConn = this;
2742
2743             if (callback && callback.timeout) {
2744                 
2745                 this.timeout[o.tId] = window.setTimeout(function() {
2746                     oConn.abort(o, callback, true);
2747                 }, callback.timeout);
2748             }
2749
2750             this.poll[o.tId] = window.setInterval(
2751                     function() {
2752                         if (o.conn && o.conn.readyState == 4) {
2753                             window.clearInterval(oConn.poll[o.tId]);
2754                             delete oConn.poll[o.tId];
2755
2756                             if(callback && callback.timeout) {
2757                                 window.clearTimeout(oConn.timeout[o.tId]);
2758                                 delete oConn.timeout[o.tId];
2759                             }
2760
2761                             oConn.handleTransactionResponse(o, callback);
2762                         }
2763                     }
2764                     , this.pollInterval);
2765         },
2766
2767         handleTransactionResponse:function(o, callback, isAbort)
2768         {
2769
2770             if (!callback) {
2771                 this.releaseObject(o);
2772                 return;
2773             }
2774
2775             var httpStatus, responseObject;
2776
2777             try
2778             {
2779                 if (o.conn.status !== undefined && o.conn.status != 0) {
2780                     httpStatus = o.conn.status;
2781                 }
2782                 else {
2783                     httpStatus = 13030;
2784                 }
2785             }
2786             catch(e) {
2787
2788
2789                 httpStatus = 13030;
2790             }
2791
2792             if (httpStatus >= 200 && httpStatus < 300) {
2793                 responseObject = this.createResponseObject(o, callback.argument);
2794                 if (callback.success) {
2795                     if (!callback.scope) {
2796                         callback.success(responseObject);
2797                     }
2798                     else {
2799
2800
2801                         callback.success.apply(callback.scope, [responseObject]);
2802                     }
2803                 }
2804             }
2805             else {
2806                 switch (httpStatus) {
2807
2808                     case 12002:
2809                     case 12029:
2810                     case 12030:
2811                     case 12031:
2812                     case 12152:
2813                     case 13030:
2814                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2815                         if (callback.failure) {
2816                             if (!callback.scope) {
2817                                 callback.failure(responseObject);
2818                             }
2819                             else {
2820                                 callback.failure.apply(callback.scope, [responseObject]);
2821                             }
2822                         }
2823                         break;
2824                     default:
2825                         responseObject = this.createResponseObject(o, callback.argument);
2826                         if (callback.failure) {
2827                             if (!callback.scope) {
2828                                 callback.failure(responseObject);
2829                             }
2830                             else {
2831                                 callback.failure.apply(callback.scope, [responseObject]);
2832                             }
2833                         }
2834                 }
2835             }
2836
2837             this.releaseObject(o);
2838             responseObject = null;
2839         },
2840
2841         createResponseObject:function(o, callbackArg)
2842         {
2843             var obj = {};
2844             var headerObj = {};
2845
2846             try
2847             {
2848                 var headerStr = o.conn.getAllResponseHeaders();
2849                 var header = headerStr.split('\n');
2850                 for (var i = 0; i < header.length; i++) {
2851                     var delimitPos = header[i].indexOf(':');
2852                     if (delimitPos != -1) {
2853                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2854                     }
2855                 }
2856             }
2857             catch(e) {
2858             }
2859
2860             obj.tId = o.tId;
2861             obj.status = o.conn.status;
2862             obj.statusText = o.conn.statusText;
2863             obj.getResponseHeader = headerObj;
2864             obj.getAllResponseHeaders = headerStr;
2865             obj.responseText = o.conn.responseText;
2866             obj.responseXML = o.conn.responseXML;
2867
2868             if (typeof callbackArg !== undefined) {
2869                 obj.argument = callbackArg;
2870             }
2871
2872             return obj;
2873         },
2874
2875         createExceptionObject:function(tId, callbackArg, isAbort)
2876         {
2877             var COMM_CODE = 0;
2878             var COMM_ERROR = 'communication failure';
2879             var ABORT_CODE = -1;
2880             var ABORT_ERROR = 'transaction aborted';
2881
2882             var obj = {};
2883
2884             obj.tId = tId;
2885             if (isAbort) {
2886                 obj.status = ABORT_CODE;
2887                 obj.statusText = ABORT_ERROR;
2888             }
2889             else {
2890                 obj.status = COMM_CODE;
2891                 obj.statusText = COMM_ERROR;
2892             }
2893
2894             if (callbackArg) {
2895                 obj.argument = callbackArg;
2896             }
2897
2898             return obj;
2899         },
2900
2901         initHeader:function(label, value, isDefault)
2902         {
2903             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2904
2905             if (headerObj[label] === undefined) {
2906                 headerObj[label] = value;
2907             }
2908             else {
2909
2910
2911                 headerObj[label] = value + "," + headerObj[label];
2912             }
2913
2914             if (isDefault) {
2915                 this.hasDefaultHeaders = true;
2916             }
2917             else {
2918                 this.hasHeaders = true;
2919             }
2920         },
2921
2922
2923         setHeader:function(o)
2924         {
2925             if (this.hasDefaultHeaders) {
2926                 for (var prop in this.defaultHeaders) {
2927                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2928                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2929                     }
2930                 }
2931             }
2932
2933             if (this.hasHeaders) {
2934                 for (var prop in this.headers) {
2935                     if (this.headers.hasOwnProperty(prop)) {
2936                         o.conn.setRequestHeader(prop, this.headers[prop]);
2937                     }
2938                 }
2939                 this.headers = {};
2940                 this.hasHeaders = false;
2941             }
2942         },
2943
2944         resetDefaultHeaders:function() {
2945             delete this.defaultHeaders;
2946             this.defaultHeaders = {};
2947             this.hasDefaultHeaders = false;
2948         },
2949
2950         abort:function(o, callback, isTimeout)
2951         {
2952             if(this.isCallInProgress(o)) {
2953                 o.conn.abort();
2954                 window.clearInterval(this.poll[o.tId]);
2955                 delete this.poll[o.tId];
2956                 if (isTimeout) {
2957                     delete this.timeout[o.tId];
2958                 }
2959
2960                 this.handleTransactionResponse(o, callback, true);
2961
2962                 return true;
2963             }
2964             else {
2965                 return false;
2966             }
2967         },
2968
2969
2970         isCallInProgress:function(o)
2971         {
2972             if (o && o.conn) {
2973                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2974             }
2975             else {
2976
2977                 return false;
2978             }
2979         },
2980
2981
2982         releaseObject:function(o)
2983         {
2984
2985             o.conn = null;
2986
2987             o = null;
2988         },
2989
2990         activeX:[
2991         'MSXML2.XMLHTTP.3.0',
2992         'MSXML2.XMLHTTP',
2993         'Microsoft.XMLHTTP'
2994         ]
2995
2996
2997     };
2998 })();/*
2999  * Portions of this file are based on pieces of Yahoo User Interface Library
3000  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3001  * YUI licensed under the BSD License:
3002  * http://developer.yahoo.net/yui/license.txt
3003  * <script type="text/javascript">
3004  *
3005  */
3006
3007 Roo.lib.Region = function(t, r, b, l) {
3008     this.top = t;
3009     this[1] = t;
3010     this.right = r;
3011     this.bottom = b;
3012     this.left = l;
3013     this[0] = l;
3014 };
3015
3016
3017 Roo.lib.Region.prototype = {
3018     contains : function(region) {
3019         return ( region.left >= this.left &&
3020                  region.right <= this.right &&
3021                  region.top >= this.top &&
3022                  region.bottom <= this.bottom    );
3023
3024     },
3025
3026     getArea : function() {
3027         return ( (this.bottom - this.top) * (this.right - this.left) );
3028     },
3029
3030     intersect : function(region) {
3031         var t = Math.max(this.top, region.top);
3032         var r = Math.min(this.right, region.right);
3033         var b = Math.min(this.bottom, region.bottom);
3034         var l = Math.max(this.left, region.left);
3035
3036         if (b >= t && r >= l) {
3037             return new Roo.lib.Region(t, r, b, l);
3038         } else {
3039             return null;
3040         }
3041     },
3042     union : function(region) {
3043         var t = Math.min(this.top, region.top);
3044         var r = Math.max(this.right, region.right);
3045         var b = Math.max(this.bottom, region.bottom);
3046         var l = Math.min(this.left, region.left);
3047
3048         return new Roo.lib.Region(t, r, b, l);
3049     },
3050
3051     adjust : function(t, l, b, r) {
3052         this.top += t;
3053         this.left += l;
3054         this.right += r;
3055         this.bottom += b;
3056         return this;
3057     }
3058 };
3059
3060 Roo.lib.Region.getRegion = function(el) {
3061     var p = Roo.lib.Dom.getXY(el);
3062
3063     var t = p[1];
3064     var r = p[0] + el.offsetWidth;
3065     var b = p[1] + el.offsetHeight;
3066     var l = p[0];
3067
3068     return new Roo.lib.Region(t, r, b, l);
3069 };
3070 /*
3071  * Portions of this file are based on pieces of Yahoo User Interface Library
3072  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3073  * YUI licensed under the BSD License:
3074  * http://developer.yahoo.net/yui/license.txt
3075  * <script type="text/javascript">
3076  *
3077  */
3078 //@@dep Roo.lib.Region
3079
3080
3081 Roo.lib.Point = function(x, y) {
3082     if (x instanceof Array) {
3083         y = x[1];
3084         x = x[0];
3085     }
3086     this.x = this.right = this.left = this[0] = x;
3087     this.y = this.top = this.bottom = this[1] = y;
3088 };
3089
3090 Roo.lib.Point.prototype = new Roo.lib.Region();
3091 /*
3092  * Portions of this file are based on pieces of Yahoo User Interface Library
3093  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3094  * YUI licensed under the BSD License:
3095  * http://developer.yahoo.net/yui/license.txt
3096  * <script type="text/javascript">
3097  *
3098  */
3099  
3100 (function() {   
3101
3102     Roo.lib.Anim = {
3103         scroll : function(el, args, duration, easing, cb, scope) {
3104             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3105         },
3106
3107         motion : function(el, args, duration, easing, cb, scope) {
3108             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3109         },
3110
3111         color : function(el, args, duration, easing, cb, scope) {
3112             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3113         },
3114
3115         run : function(el, args, duration, easing, cb, scope, type) {
3116             type = type || Roo.lib.AnimBase;
3117             if (typeof easing == "string") {
3118                 easing = Roo.lib.Easing[easing];
3119             }
3120             var anim = new type(el, args, duration, easing);
3121             anim.animateX(function() {
3122                 Roo.callback(cb, scope);
3123             });
3124             return anim;
3125         }
3126     };
3127 })();/*
3128  * Portions of this file are based on pieces of Yahoo User Interface Library
3129  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3130  * YUI licensed under the BSD License:
3131  * http://developer.yahoo.net/yui/license.txt
3132  * <script type="text/javascript">
3133  *
3134  */
3135
3136 (function() {    
3137     var libFlyweight;
3138     
3139     function fly(el) {
3140         if (!libFlyweight) {
3141             libFlyweight = new Roo.Element.Flyweight();
3142         }
3143         libFlyweight.dom = el;
3144         return libFlyweight;
3145     }
3146
3147     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3148     
3149    
3150     
3151     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3152         if (el) {
3153             this.init(el, attributes, duration, method);
3154         }
3155     };
3156
3157     Roo.lib.AnimBase.fly = fly;
3158     
3159     
3160     
3161     Roo.lib.AnimBase.prototype = {
3162
3163         toString: function() {
3164             var el = this.getEl();
3165             var id = el.id || el.tagName;
3166             return ("Anim " + id);
3167         },
3168
3169         patterns: {
3170             noNegatives:        /width|height|opacity|padding/i,
3171             offsetAttribute:  /^((width|height)|(top|left))$/,
3172             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3173             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3174         },
3175
3176
3177         doMethod: function(attr, start, end) {
3178             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3179         },
3180
3181
3182         setAttribute: function(attr, val, unit) {
3183             if (this.patterns.noNegatives.test(attr)) {
3184                 val = (val > 0) ? val : 0;
3185             }
3186
3187             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3188         },
3189
3190
3191         getAttribute: function(attr) {
3192             var el = this.getEl();
3193             var val = fly(el).getStyle(attr);
3194
3195             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3196                 return parseFloat(val);
3197             }
3198
3199             var a = this.patterns.offsetAttribute.exec(attr) || [];
3200             var pos = !!( a[3] );
3201             var box = !!( a[2] );
3202
3203
3204             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3205                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3206             } else {
3207                 val = 0;
3208             }
3209
3210             return val;
3211         },
3212
3213
3214         getDefaultUnit: function(attr) {
3215             if (this.patterns.defaultUnit.test(attr)) {
3216                 return 'px';
3217             }
3218
3219             return '';
3220         },
3221
3222         animateX : function(callback, scope) {
3223             var f = function() {
3224                 this.onComplete.removeListener(f);
3225                 if (typeof callback == "function") {
3226                     callback.call(scope || this, this);
3227                 }
3228             };
3229             this.onComplete.addListener(f, this);
3230             this.animate();
3231         },
3232
3233
3234         setRuntimeAttribute: function(attr) {
3235             var start;
3236             var end;
3237             var attributes = this.attributes;
3238
3239             this.runtimeAttributes[attr] = {};
3240
3241             var isset = function(prop) {
3242                 return (typeof prop !== 'undefined');
3243             };
3244
3245             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3246                 return false;
3247             }
3248
3249             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3250
3251
3252             if (isset(attributes[attr]['to'])) {
3253                 end = attributes[attr]['to'];
3254             } else if (isset(attributes[attr]['by'])) {
3255                 if (start.constructor == Array) {
3256                     end = [];
3257                     for (var i = 0, len = start.length; i < len; ++i) {
3258                         end[i] = start[i] + attributes[attr]['by'][i];
3259                     }
3260                 } else {
3261                     end = start + attributes[attr]['by'];
3262                 }
3263             }
3264
3265             this.runtimeAttributes[attr].start = start;
3266             this.runtimeAttributes[attr].end = end;
3267
3268
3269             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3270         },
3271
3272
3273         init: function(el, attributes, duration, method) {
3274
3275             var isAnimated = false;
3276
3277
3278             var startTime = null;
3279
3280
3281             var actualFrames = 0;
3282
3283
3284             el = Roo.getDom(el);
3285
3286
3287             this.attributes = attributes || {};
3288
3289
3290             this.duration = duration || 1;
3291
3292
3293             this.method = method || Roo.lib.Easing.easeNone;
3294
3295
3296             this.useSeconds = true;
3297
3298
3299             this.currentFrame = 0;
3300
3301
3302             this.totalFrames = Roo.lib.AnimMgr.fps;
3303
3304
3305             this.getEl = function() {
3306                 return el;
3307             };
3308
3309
3310             this.isAnimated = function() {
3311                 return isAnimated;
3312             };
3313
3314
3315             this.getStartTime = function() {
3316                 return startTime;
3317             };
3318
3319             this.runtimeAttributes = {};
3320
3321
3322             this.animate = function() {
3323                 if (this.isAnimated()) {
3324                     return false;
3325                 }
3326
3327                 this.currentFrame = 0;
3328
3329                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3330
3331                 Roo.lib.AnimMgr.registerElement(this);
3332             };
3333
3334
3335             this.stop = function(finish) {
3336                 if (finish) {
3337                     this.currentFrame = this.totalFrames;
3338                     this._onTween.fire();
3339                 }
3340                 Roo.lib.AnimMgr.stop(this);
3341             };
3342
3343             var onStart = function() {
3344                 this.onStart.fire();
3345
3346                 this.runtimeAttributes = {};
3347                 for (var attr in this.attributes) {
3348                     this.setRuntimeAttribute(attr);
3349                 }
3350
3351                 isAnimated = true;
3352                 actualFrames = 0;
3353                 startTime = new Date();
3354             };
3355
3356
3357             var onTween = function() {
3358                 var data = {
3359                     duration: new Date() - this.getStartTime(),
3360                     currentFrame: this.currentFrame
3361                 };
3362
3363                 data.toString = function() {
3364                     return (
3365                             'duration: ' + data.duration +
3366                             ', currentFrame: ' + data.currentFrame
3367                             );
3368                 };
3369
3370                 this.onTween.fire(data);
3371
3372                 var runtimeAttributes = this.runtimeAttributes;
3373
3374                 for (var attr in runtimeAttributes) {
3375                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3376                 }
3377
3378                 actualFrames += 1;
3379             };
3380
3381             var onComplete = function() {
3382                 var actual_duration = (new Date() - startTime) / 1000 ;
3383
3384                 var data = {
3385                     duration: actual_duration,
3386                     frames: actualFrames,
3387                     fps: actualFrames / actual_duration
3388                 };
3389
3390                 data.toString = function() {
3391                     return (
3392                             'duration: ' + data.duration +
3393                             ', frames: ' + data.frames +
3394                             ', fps: ' + data.fps
3395                             );
3396                 };
3397
3398                 isAnimated = false;
3399                 actualFrames = 0;
3400                 this.onComplete.fire(data);
3401             };
3402
3403
3404             this._onStart = new Roo.util.Event(this);
3405             this.onStart = new Roo.util.Event(this);
3406             this.onTween = new Roo.util.Event(this);
3407             this._onTween = new Roo.util.Event(this);
3408             this.onComplete = new Roo.util.Event(this);
3409             this._onComplete = new Roo.util.Event(this);
3410             this._onStart.addListener(onStart);
3411             this._onTween.addListener(onTween);
3412             this._onComplete.addListener(onComplete);
3413         }
3414     };
3415 })();
3416 /*
3417  * Portions of this file are based on pieces of Yahoo User Interface Library
3418  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3419  * YUI licensed under the BSD License:
3420  * http://developer.yahoo.net/yui/license.txt
3421  * <script type="text/javascript">
3422  *
3423  */
3424
3425 Roo.lib.AnimMgr = new function() {
3426
3427     var thread = null;
3428
3429
3430     var queue = [];
3431
3432
3433     var tweenCount = 0;
3434
3435
3436     this.fps = 1000;
3437
3438
3439     this.delay = 1;
3440
3441
3442     this.registerElement = function(tween) {
3443         queue[queue.length] = tween;
3444         tweenCount += 1;
3445         tween._onStart.fire();
3446         this.start();
3447     };
3448
3449
3450     this.unRegister = function(tween, index) {
3451         tween._onComplete.fire();
3452         index = index || getIndex(tween);
3453         if (index != -1) {
3454             queue.splice(index, 1);
3455         }
3456
3457         tweenCount -= 1;
3458         if (tweenCount <= 0) {
3459             this.stop();
3460         }
3461     };
3462
3463
3464     this.start = function() {
3465         if (thread === null) {
3466             thread = setInterval(this.run, this.delay);
3467         }
3468     };
3469
3470
3471     this.stop = function(tween) {
3472         if (!tween) {
3473             clearInterval(thread);
3474
3475             for (var i = 0, len = queue.length; i < len; ++i) {
3476                 if (queue[0].isAnimated()) {
3477                     this.unRegister(queue[0], 0);
3478                 }
3479             }
3480
3481             queue = [];
3482             thread = null;
3483             tweenCount = 0;
3484         }
3485         else {
3486             this.unRegister(tween);
3487         }
3488     };
3489
3490
3491     this.run = function() {
3492         for (var i = 0, len = queue.length; i < len; ++i) {
3493             var tween = queue[i];
3494             if (!tween || !tween.isAnimated()) {
3495                 continue;
3496             }
3497
3498             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3499             {
3500                 tween.currentFrame += 1;
3501
3502                 if (tween.useSeconds) {
3503                     correctFrame(tween);
3504                 }
3505                 tween._onTween.fire();
3506             }
3507             else {
3508                 Roo.lib.AnimMgr.stop(tween, i);
3509             }
3510         }
3511     };
3512
3513     var getIndex = function(anim) {
3514         for (var i = 0, len = queue.length; i < len; ++i) {
3515             if (queue[i] == anim) {
3516                 return i;
3517             }
3518         }
3519         return -1;
3520     };
3521
3522
3523     var correctFrame = function(tween) {
3524         var frames = tween.totalFrames;
3525         var frame = tween.currentFrame;
3526         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3527         var elapsed = (new Date() - tween.getStartTime());
3528         var tweak = 0;
3529
3530         if (elapsed < tween.duration * 1000) {
3531             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3532         } else {
3533             tweak = frames - (frame + 1);
3534         }
3535         if (tweak > 0 && isFinite(tweak)) {
3536             if (tween.currentFrame + tweak >= frames) {
3537                 tweak = frames - (frame + 1);
3538             }
3539
3540             tween.currentFrame += tweak;
3541         }
3542     };
3543 };
3544
3545     /*
3546  * Portions of this file are based on pieces of Yahoo User Interface Library
3547  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3548  * YUI licensed under the BSD License:
3549  * http://developer.yahoo.net/yui/license.txt
3550  * <script type="text/javascript">
3551  *
3552  */
3553 Roo.lib.Bezier = new function() {
3554
3555         this.getPosition = function(points, t) {
3556             var n = points.length;
3557             var tmp = [];
3558
3559             for (var i = 0; i < n; ++i) {
3560                 tmp[i] = [points[i][0], points[i][1]];
3561             }
3562
3563             for (var j = 1; j < n; ++j) {
3564                 for (i = 0; i < n - j; ++i) {
3565                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3566                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3567                 }
3568             }
3569
3570             return [ tmp[0][0], tmp[0][1] ];
3571
3572         };
3573     };/*
3574  * Portions of this file are based on pieces of Yahoo User Interface Library
3575  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3576  * YUI licensed under the BSD License:
3577  * http://developer.yahoo.net/yui/license.txt
3578  * <script type="text/javascript">
3579  *
3580  */
3581 (function() {
3582
3583     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3584         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3585     };
3586
3587     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3588
3589     var fly = Roo.lib.AnimBase.fly;
3590     var Y = Roo.lib;
3591     var superclass = Y.ColorAnim.superclass;
3592     var proto = Y.ColorAnim.prototype;
3593
3594     proto.toString = function() {
3595         var el = this.getEl();
3596         var id = el.id || el.tagName;
3597         return ("ColorAnim " + id);
3598     };
3599
3600     proto.patterns.color = /color$/i;
3601     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3602     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3603     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3604     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3605
3606
3607     proto.parseColor = function(s) {
3608         if (s.length == 3) {
3609             return s;
3610         }
3611
3612         var c = this.patterns.hex.exec(s);
3613         if (c && c.length == 4) {
3614             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3615         }
3616
3617         c = this.patterns.rgb.exec(s);
3618         if (c && c.length == 4) {
3619             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3620         }
3621
3622         c = this.patterns.hex3.exec(s);
3623         if (c && c.length == 4) {
3624             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3625         }
3626
3627         return null;
3628     };
3629     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3630     proto.getAttribute = function(attr) {
3631         var el = this.getEl();
3632         if (this.patterns.color.test(attr)) {
3633             var val = fly(el).getStyle(attr);
3634
3635             if (this.patterns.transparent.test(val)) {
3636                 var parent = el.parentNode;
3637                 val = fly(parent).getStyle(attr);
3638
3639                 while (parent && this.patterns.transparent.test(val)) {
3640                     parent = parent.parentNode;
3641                     val = fly(parent).getStyle(attr);
3642                     if (parent.tagName.toUpperCase() == 'HTML') {
3643                         val = '#fff';
3644                     }
3645                 }
3646             }
3647         } else {
3648             val = superclass.getAttribute.call(this, attr);
3649         }
3650
3651         return val;
3652     };
3653     proto.getAttribute = function(attr) {
3654         var el = this.getEl();
3655         if (this.patterns.color.test(attr)) {
3656             var val = fly(el).getStyle(attr);
3657
3658             if (this.patterns.transparent.test(val)) {
3659                 var parent = el.parentNode;
3660                 val = fly(parent).getStyle(attr);
3661
3662                 while (parent && this.patterns.transparent.test(val)) {
3663                     parent = parent.parentNode;
3664                     val = fly(parent).getStyle(attr);
3665                     if (parent.tagName.toUpperCase() == 'HTML') {
3666                         val = '#fff';
3667                     }
3668                 }
3669             }
3670         } else {
3671             val = superclass.getAttribute.call(this, attr);
3672         }
3673
3674         return val;
3675     };
3676
3677     proto.doMethod = function(attr, start, end) {
3678         var val;
3679
3680         if (this.patterns.color.test(attr)) {
3681             val = [];
3682             for (var i = 0, len = start.length; i < len; ++i) {
3683                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3684             }
3685
3686             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3687         }
3688         else {
3689             val = superclass.doMethod.call(this, attr, start, end);
3690         }
3691
3692         return val;
3693     };
3694
3695     proto.setRuntimeAttribute = function(attr) {
3696         superclass.setRuntimeAttribute.call(this, attr);
3697
3698         if (this.patterns.color.test(attr)) {
3699             var attributes = this.attributes;
3700             var start = this.parseColor(this.runtimeAttributes[attr].start);
3701             var end = this.parseColor(this.runtimeAttributes[attr].end);
3702
3703             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3704                 end = this.parseColor(attributes[attr].by);
3705
3706                 for (var i = 0, len = start.length; i < len; ++i) {
3707                     end[i] = start[i] + end[i];
3708                 }
3709             }
3710
3711             this.runtimeAttributes[attr].start = start;
3712             this.runtimeAttributes[attr].end = end;
3713         }
3714     };
3715 })();
3716
3717 /*
3718  * Portions of this file are based on pieces of Yahoo User Interface Library
3719  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3720  * YUI licensed under the BSD License:
3721  * http://developer.yahoo.net/yui/license.txt
3722  * <script type="text/javascript">
3723  *
3724  */
3725 Roo.lib.Easing = {
3726
3727
3728     easeNone: function (t, b, c, d) {
3729         return c * t / d + b;
3730     },
3731
3732
3733     easeIn: function (t, b, c, d) {
3734         return c * (t /= d) * t + b;
3735     },
3736
3737
3738     easeOut: function (t, b, c, d) {
3739         return -c * (t /= d) * (t - 2) + b;
3740     },
3741
3742
3743     easeBoth: function (t, b, c, d) {
3744         if ((t /= d / 2) < 1) {
3745             return c / 2 * t * t + b;
3746         }
3747
3748         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3749     },
3750
3751
3752     easeInStrong: function (t, b, c, d) {
3753         return c * (t /= d) * t * t * t + b;
3754     },
3755
3756
3757     easeOutStrong: function (t, b, c, d) {
3758         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3759     },
3760
3761
3762     easeBothStrong: function (t, b, c, d) {
3763         if ((t /= d / 2) < 1) {
3764             return c / 2 * t * t * t * t + b;
3765         }
3766
3767         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3768     },
3769
3770
3771
3772     elasticIn: function (t, b, c, d, a, p) {
3773         if (t == 0) {
3774             return b;
3775         }
3776         if ((t /= d) == 1) {
3777             return b + c;
3778         }
3779         if (!p) {
3780             p = d * .3;
3781         }
3782
3783         if (!a || a < Math.abs(c)) {
3784             a = c;
3785             var s = p / 4;
3786         }
3787         else {
3788             var s = p / (2 * Math.PI) * Math.asin(c / a);
3789         }
3790
3791         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3792     },
3793
3794
3795     elasticOut: function (t, b, c, d, a, p) {
3796         if (t == 0) {
3797             return b;
3798         }
3799         if ((t /= d) == 1) {
3800             return b + c;
3801         }
3802         if (!p) {
3803             p = d * .3;
3804         }
3805
3806         if (!a || a < Math.abs(c)) {
3807             a = c;
3808             var s = p / 4;
3809         }
3810         else {
3811             var s = p / (2 * Math.PI) * Math.asin(c / a);
3812         }
3813
3814         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3815     },
3816
3817
3818     elasticBoth: function (t, b, c, d, a, p) {
3819         if (t == 0) {
3820             return b;
3821         }
3822
3823         if ((t /= d / 2) == 2) {
3824             return b + c;
3825         }
3826
3827         if (!p) {
3828             p = d * (.3 * 1.5);
3829         }
3830
3831         if (!a || a < Math.abs(c)) {
3832             a = c;
3833             var s = p / 4;
3834         }
3835         else {
3836             var s = p / (2 * Math.PI) * Math.asin(c / a);
3837         }
3838
3839         if (t < 1) {
3840             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3841                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3842         }
3843         return a * Math.pow(2, -10 * (t -= 1)) *
3844                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3845     },
3846
3847
3848
3849     backIn: function (t, b, c, d, s) {
3850         if (typeof s == 'undefined') {
3851             s = 1.70158;
3852         }
3853         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3854     },
3855
3856
3857     backOut: function (t, b, c, d, s) {
3858         if (typeof s == 'undefined') {
3859             s = 1.70158;
3860         }
3861         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3862     },
3863
3864
3865     backBoth: function (t, b, c, d, s) {
3866         if (typeof s == 'undefined') {
3867             s = 1.70158;
3868         }
3869
3870         if ((t /= d / 2 ) < 1) {
3871             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3872         }
3873         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3874     },
3875
3876
3877     bounceIn: function (t, b, c, d) {
3878         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3879     },
3880
3881
3882     bounceOut: function (t, b, c, d) {
3883         if ((t /= d) < (1 / 2.75)) {
3884             return c * (7.5625 * t * t) + b;
3885         } else if (t < (2 / 2.75)) {
3886             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3887         } else if (t < (2.5 / 2.75)) {
3888             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3889         }
3890         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3891     },
3892
3893
3894     bounceBoth: function (t, b, c, d) {
3895         if (t < d / 2) {
3896             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3897         }
3898         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3899     }
3900 };/*
3901  * Portions of this file are based on pieces of Yahoo User Interface Library
3902  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3903  * YUI licensed under the BSD License:
3904  * http://developer.yahoo.net/yui/license.txt
3905  * <script type="text/javascript">
3906  *
3907  */
3908     (function() {
3909         Roo.lib.Motion = function(el, attributes, duration, method) {
3910             if (el) {
3911                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3912             }
3913         };
3914
3915         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3916
3917
3918         var Y = Roo.lib;
3919         var superclass = Y.Motion.superclass;
3920         var proto = Y.Motion.prototype;
3921
3922         proto.toString = function() {
3923             var el = this.getEl();
3924             var id = el.id || el.tagName;
3925             return ("Motion " + id);
3926         };
3927
3928         proto.patterns.points = /^points$/i;
3929
3930         proto.setAttribute = function(attr, val, unit) {
3931             if (this.patterns.points.test(attr)) {
3932                 unit = unit || 'px';
3933                 superclass.setAttribute.call(this, 'left', val[0], unit);
3934                 superclass.setAttribute.call(this, 'top', val[1], unit);
3935             } else {
3936                 superclass.setAttribute.call(this, attr, val, unit);
3937             }
3938         };
3939
3940         proto.getAttribute = function(attr) {
3941             if (this.patterns.points.test(attr)) {
3942                 var val = [
3943                         superclass.getAttribute.call(this, 'left'),
3944                         superclass.getAttribute.call(this, 'top')
3945                         ];
3946             } else {
3947                 val = superclass.getAttribute.call(this, attr);
3948             }
3949
3950             return val;
3951         };
3952
3953         proto.doMethod = function(attr, start, end) {
3954             var val = null;
3955
3956             if (this.patterns.points.test(attr)) {
3957                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3958                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3959             } else {
3960                 val = superclass.doMethod.call(this, attr, start, end);
3961             }
3962             return val;
3963         };
3964
3965         proto.setRuntimeAttribute = function(attr) {
3966             if (this.patterns.points.test(attr)) {
3967                 var el = this.getEl();
3968                 var attributes = this.attributes;
3969                 var start;
3970                 var control = attributes['points']['control'] || [];
3971                 var end;
3972                 var i, len;
3973
3974                 if (control.length > 0 && !(control[0] instanceof Array)) {
3975                     control = [control];
3976                 } else {
3977                     var tmp = [];
3978                     for (i = 0,len = control.length; i < len; ++i) {
3979                         tmp[i] = control[i];
3980                     }
3981                     control = tmp;
3982                 }
3983
3984                 Roo.fly(el).position();
3985
3986                 if (isset(attributes['points']['from'])) {
3987                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3988                 }
3989                 else {
3990                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3991                 }
3992
3993                 start = this.getAttribute('points');
3994
3995
3996                 if (isset(attributes['points']['to'])) {
3997                     end = translateValues.call(this, attributes['points']['to'], start);
3998
3999                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4000                     for (i = 0,len = control.length; i < len; ++i) {
4001                         control[i] = translateValues.call(this, control[i], start);
4002                     }
4003
4004
4005                 } else if (isset(attributes['points']['by'])) {
4006                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4007
4008                     for (i = 0,len = control.length; i < len; ++i) {
4009                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4010                     }
4011                 }
4012
4013                 this.runtimeAttributes[attr] = [start];
4014
4015                 if (control.length > 0) {
4016                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4017                 }
4018
4019                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4020             }
4021             else {
4022                 superclass.setRuntimeAttribute.call(this, attr);
4023             }
4024         };
4025
4026         var translateValues = function(val, start) {
4027             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4028             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4029
4030             return val;
4031         };
4032
4033         var isset = function(prop) {
4034             return (typeof prop !== 'undefined');
4035         };
4036     })();
4037 /*
4038  * Portions of this file are based on pieces of Yahoo User Interface Library
4039  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4040  * YUI licensed under the BSD License:
4041  * http://developer.yahoo.net/yui/license.txt
4042  * <script type="text/javascript">
4043  *
4044  */
4045     (function() {
4046         Roo.lib.Scroll = function(el, attributes, duration, method) {
4047             if (el) {
4048                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4049             }
4050         };
4051
4052         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4053
4054
4055         var Y = Roo.lib;
4056         var superclass = Y.Scroll.superclass;
4057         var proto = Y.Scroll.prototype;
4058
4059         proto.toString = function() {
4060             var el = this.getEl();
4061             var id = el.id || el.tagName;
4062             return ("Scroll " + id);
4063         };
4064
4065         proto.doMethod = function(attr, start, end) {
4066             var val = null;
4067
4068             if (attr == 'scroll') {
4069                 val = [
4070                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4071                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4072                         ];
4073
4074             } else {
4075                 val = superclass.doMethod.call(this, attr, start, end);
4076             }
4077             return val;
4078         };
4079
4080         proto.getAttribute = function(attr) {
4081             var val = null;
4082             var el = this.getEl();
4083
4084             if (attr == 'scroll') {
4085                 val = [ el.scrollLeft, el.scrollTop ];
4086             } else {
4087                 val = superclass.getAttribute.call(this, attr);
4088             }
4089
4090             return val;
4091         };
4092
4093         proto.setAttribute = function(attr, val, unit) {
4094             var el = this.getEl();
4095
4096             if (attr == 'scroll') {
4097                 el.scrollLeft = val[0];
4098                 el.scrollTop = val[1];
4099             } else {
4100                 superclass.setAttribute.call(this, attr, val, unit);
4101             }
4102         };
4103     })();
4104 /*
4105  * Based on:
4106  * Ext JS Library 1.1.1
4107  * Copyright(c) 2006-2007, Ext JS, LLC.
4108  *
4109  * Originally Released Under LGPL - original licence link has changed is not relivant.
4110  *
4111  * Fork - LGPL
4112  * <script type="text/javascript">
4113  */
4114
4115
4116 // nasty IE9 hack - what a pile of crap that is..
4117
4118  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4119     Range.prototype.createContextualFragment = function (html) {
4120         var doc = window.document;
4121         var container = doc.createElement("div");
4122         container.innerHTML = html;
4123         var frag = doc.createDocumentFragment(), n;
4124         while ((n = container.firstChild)) {
4125             frag.appendChild(n);
4126         }
4127         return frag;
4128     };
4129 }
4130
4131 /**
4132  * @class Roo.DomHelper
4133  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4134  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4135  * @singleton
4136  */
4137 Roo.DomHelper = function(){
4138     var tempTableEl = null;
4139     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4140     var tableRe = /^table|tbody|tr|td$/i;
4141     var xmlns = {};
4142     // build as innerHTML where available
4143     /** @ignore */
4144     var createHtml = function(o){
4145         if(typeof o == 'string'){
4146             return o;
4147         }
4148         var b = "";
4149         if(!o.tag){
4150             o.tag = "div";
4151         }
4152         b += "<" + o.tag;
4153         for(var attr in o){
4154             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4155             if(attr == "style"){
4156                 var s = o["style"];
4157                 if(typeof s == "function"){
4158                     s = s.call();
4159                 }
4160                 if(typeof s == "string"){
4161                     b += ' style="' + s + '"';
4162                 }else if(typeof s == "object"){
4163                     b += ' style="';
4164                     for(var key in s){
4165                         if(typeof s[key] != "function"){
4166                             b += key + ":" + s[key] + ";";
4167                         }
4168                     }
4169                     b += '"';
4170                 }
4171             }else{
4172                 if(attr == "cls"){
4173                     b += ' class="' + o["cls"] + '"';
4174                 }else if(attr == "htmlFor"){
4175                     b += ' for="' + o["htmlFor"] + '"';
4176                 }else{
4177                     b += " " + attr + '="' + o[attr] + '"';
4178                 }
4179             }
4180         }
4181         if(emptyTags.test(o.tag)){
4182             b += "/>";
4183         }else{
4184             b += ">";
4185             var cn = o.children || o.cn;
4186             if(cn){
4187                 //http://bugs.kde.org/show_bug.cgi?id=71506
4188                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4189                     for(var i = 0, len = cn.length; i < len; i++) {
4190                         b += createHtml(cn[i], b);
4191                     }
4192                 }else{
4193                     b += createHtml(cn, b);
4194                 }
4195             }
4196             if(o.html){
4197                 b += o.html;
4198             }
4199             b += "</" + o.tag + ">";
4200         }
4201         return b;
4202     };
4203
4204     // build as dom
4205     /** @ignore */
4206     var createDom = function(o, parentNode){
4207          
4208         // defininition craeted..
4209         var ns = false;
4210         if (o.ns && o.ns != 'html') {
4211                
4212             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4213                 xmlns[o.ns] = o.xmlns;
4214                 ns = o.xmlns;
4215             }
4216             if (typeof(xmlns[o.ns]) == 'undefined') {
4217                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4218             }
4219             ns = xmlns[o.ns];
4220         }
4221         
4222         
4223         if (typeof(o) == 'string') {
4224             return parentNode.appendChild(document.createTextNode(o));
4225         }
4226         o.tag = o.tag || div;
4227         if (o.ns && Roo.isIE) {
4228             ns = false;
4229             o.tag = o.ns + ':' + o.tag;
4230             
4231         }
4232         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4233         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4234         for(var attr in o){
4235             
4236             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4237                     attr == "style" || typeof o[attr] == "function") continue;
4238                     
4239             if(attr=="cls" && Roo.isIE){
4240                 el.className = o["cls"];
4241             }else{
4242                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4243                 else { 
4244                     el[attr] = o[attr];
4245                 }
4246             }
4247         }
4248         Roo.DomHelper.applyStyles(el, o.style);
4249         var cn = o.children || o.cn;
4250         if(cn){
4251             //http://bugs.kde.org/show_bug.cgi?id=71506
4252              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4253                 for(var i = 0, len = cn.length; i < len; i++) {
4254                     createDom(cn[i], el);
4255                 }
4256             }else{
4257                 createDom(cn, el);
4258             }
4259         }
4260         if(o.html){
4261             el.innerHTML = o.html;
4262         }
4263         if(parentNode){
4264            parentNode.appendChild(el);
4265         }
4266         return el;
4267     };
4268
4269     var ieTable = function(depth, s, h, e){
4270         tempTableEl.innerHTML = [s, h, e].join('');
4271         var i = -1, el = tempTableEl;
4272         while(++i < depth){
4273             el = el.firstChild;
4274         }
4275         return el;
4276     };
4277
4278     // kill repeat to save bytes
4279     var ts = '<table>',
4280         te = '</table>',
4281         tbs = ts+'<tbody>',
4282         tbe = '</tbody>'+te,
4283         trs = tbs + '<tr>',
4284         tre = '</tr>'+tbe;
4285
4286     /**
4287      * @ignore
4288      * Nasty code for IE's broken table implementation
4289      */
4290     var insertIntoTable = function(tag, where, el, html){
4291         if(!tempTableEl){
4292             tempTableEl = document.createElement('div');
4293         }
4294         var node;
4295         var before = null;
4296         if(tag == 'td'){
4297             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4298                 return;
4299             }
4300             if(where == 'beforebegin'){
4301                 before = el;
4302                 el = el.parentNode;
4303             } else{
4304                 before = el.nextSibling;
4305                 el = el.parentNode;
4306             }
4307             node = ieTable(4, trs, html, tre);
4308         }
4309         else if(tag == 'tr'){
4310             if(where == 'beforebegin'){
4311                 before = el;
4312                 el = el.parentNode;
4313                 node = ieTable(3, tbs, html, tbe);
4314             } else if(where == 'afterend'){
4315                 before = el.nextSibling;
4316                 el = el.parentNode;
4317                 node = ieTable(3, tbs, html, tbe);
4318             } else{ // INTO a TR
4319                 if(where == 'afterbegin'){
4320                     before = el.firstChild;
4321                 }
4322                 node = ieTable(4, trs, html, tre);
4323             }
4324         } else if(tag == 'tbody'){
4325             if(where == 'beforebegin'){
4326                 before = el;
4327                 el = el.parentNode;
4328                 node = ieTable(2, ts, html, te);
4329             } else if(where == 'afterend'){
4330                 before = el.nextSibling;
4331                 el = el.parentNode;
4332                 node = ieTable(2, ts, html, te);
4333             } else{
4334                 if(where == 'afterbegin'){
4335                     before = el.firstChild;
4336                 }
4337                 node = ieTable(3, tbs, html, tbe);
4338             }
4339         } else{ // TABLE
4340             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4341                 return;
4342             }
4343             if(where == 'afterbegin'){
4344                 before = el.firstChild;
4345             }
4346             node = ieTable(2, ts, html, te);
4347         }
4348         el.insertBefore(node, before);
4349         return node;
4350     };
4351
4352     return {
4353     /** True to force the use of DOM instead of html fragments @type Boolean */
4354     useDom : false,
4355
4356     /**
4357      * Returns the markup for the passed Element(s) config
4358      * @param {Object} o The Dom object spec (and children)
4359      * @return {String}
4360      */
4361     markup : function(o){
4362         return createHtml(o);
4363     },
4364
4365     /**
4366      * Applies a style specification to an element
4367      * @param {String/HTMLElement} el The element to apply styles to
4368      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4369      * a function which returns such a specification.
4370      */
4371     applyStyles : function(el, styles){
4372         if(styles){
4373            el = Roo.fly(el);
4374            if(typeof styles == "string"){
4375                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4376                var matches;
4377                while ((matches = re.exec(styles)) != null){
4378                    el.setStyle(matches[1], matches[2]);
4379                }
4380            }else if (typeof styles == "object"){
4381                for (var style in styles){
4382                   el.setStyle(style, styles[style]);
4383                }
4384            }else if (typeof styles == "function"){
4385                 Roo.DomHelper.applyStyles(el, styles.call());
4386            }
4387         }
4388     },
4389
4390     /**
4391      * Inserts an HTML fragment into the Dom
4392      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4393      * @param {HTMLElement} el The context element
4394      * @param {String} html The HTML fragmenet
4395      * @return {HTMLElement} The new node
4396      */
4397     insertHtml : function(where, el, html){
4398         where = where.toLowerCase();
4399         if(el.insertAdjacentHTML){
4400             if(tableRe.test(el.tagName)){
4401                 var rs;
4402                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4403                     return rs;
4404                 }
4405             }
4406             switch(where){
4407                 case "beforebegin":
4408                     el.insertAdjacentHTML('BeforeBegin', html);
4409                     return el.previousSibling;
4410                 case "afterbegin":
4411                     el.insertAdjacentHTML('AfterBegin', html);
4412                     return el.firstChild;
4413                 case "beforeend":
4414                     el.insertAdjacentHTML('BeforeEnd', html);
4415                     return el.lastChild;
4416                 case "afterend":
4417                     el.insertAdjacentHTML('AfterEnd', html);
4418                     return el.nextSibling;
4419             }
4420             throw 'Illegal insertion point -> "' + where + '"';
4421         }
4422         var range = el.ownerDocument.createRange();
4423         var frag;
4424         switch(where){
4425              case "beforebegin":
4426                 range.setStartBefore(el);
4427                 frag = range.createContextualFragment(html);
4428                 el.parentNode.insertBefore(frag, el);
4429                 return el.previousSibling;
4430              case "afterbegin":
4431                 if(el.firstChild){
4432                     range.setStartBefore(el.firstChild);
4433                     frag = range.createContextualFragment(html);
4434                     el.insertBefore(frag, el.firstChild);
4435                     return el.firstChild;
4436                 }else{
4437                     el.innerHTML = html;
4438                     return el.firstChild;
4439                 }
4440             case "beforeend":
4441                 if(el.lastChild){
4442                     range.setStartAfter(el.lastChild);
4443                     frag = range.createContextualFragment(html);
4444                     el.appendChild(frag);
4445                     return el.lastChild;
4446                 }else{
4447                     el.innerHTML = html;
4448                     return el.lastChild;
4449                 }
4450             case "afterend":
4451                 range.setStartAfter(el);
4452                 frag = range.createContextualFragment(html);
4453                 el.parentNode.insertBefore(frag, el.nextSibling);
4454                 return el.nextSibling;
4455             }
4456             throw 'Illegal insertion point -> "' + where + '"';
4457     },
4458
4459     /**
4460      * Creates new Dom element(s) and inserts them before el
4461      * @param {String/HTMLElement/Element} el The context element
4462      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4463      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4464      * @return {HTMLElement/Roo.Element} The new node
4465      */
4466     insertBefore : function(el, o, returnElement){
4467         return this.doInsert(el, o, returnElement, "beforeBegin");
4468     },
4469
4470     /**
4471      * Creates new Dom element(s) and inserts them after el
4472      * @param {String/HTMLElement/Element} el The context element
4473      * @param {Object} o The Dom object spec (and children)
4474      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4475      * @return {HTMLElement/Roo.Element} The new node
4476      */
4477     insertAfter : function(el, o, returnElement){
4478         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4479     },
4480
4481     /**
4482      * Creates new Dom element(s) and inserts them as the first child of el
4483      * @param {String/HTMLElement/Element} el The context element
4484      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4485      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4486      * @return {HTMLElement/Roo.Element} The new node
4487      */
4488     insertFirst : function(el, o, returnElement){
4489         return this.doInsert(el, o, returnElement, "afterBegin");
4490     },
4491
4492     // private
4493     doInsert : function(el, o, returnElement, pos, sibling){
4494         el = Roo.getDom(el);
4495         var newNode;
4496         if(this.useDom || o.ns){
4497             newNode = createDom(o, null);
4498             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4499         }else{
4500             var html = createHtml(o);
4501             newNode = this.insertHtml(pos, el, html);
4502         }
4503         return returnElement ? Roo.get(newNode, true) : newNode;
4504     },
4505
4506     /**
4507      * Creates new Dom element(s) and appends them to el
4508      * @param {String/HTMLElement/Element} el The context element
4509      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4510      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4511      * @return {HTMLElement/Roo.Element} The new node
4512      */
4513     append : function(el, o, returnElement){
4514         el = Roo.getDom(el);
4515         var newNode;
4516         if(this.useDom || o.ns){
4517             newNode = createDom(o, null);
4518             el.appendChild(newNode);
4519         }else{
4520             var html = createHtml(o);
4521             newNode = this.insertHtml("beforeEnd", el, html);
4522         }
4523         return returnElement ? Roo.get(newNode, true) : newNode;
4524     },
4525
4526     /**
4527      * Creates new Dom element(s) and overwrites the contents of el with them
4528      * @param {String/HTMLElement/Element} el The context element
4529      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4530      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4531      * @return {HTMLElement/Roo.Element} The new node
4532      */
4533     overwrite : function(el, o, returnElement){
4534         el = Roo.getDom(el);
4535         if (o.ns) {
4536           
4537             while (el.childNodes.length) {
4538                 el.removeChild(el.firstChild);
4539             }
4540             createDom(o, el);
4541         } else {
4542             el.innerHTML = createHtml(o);   
4543         }
4544         
4545         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4546     },
4547
4548     /**
4549      * Creates a new Roo.DomHelper.Template from the Dom object spec
4550      * @param {Object} o The Dom object spec (and children)
4551      * @return {Roo.DomHelper.Template} The new template
4552      */
4553     createTemplate : function(o){
4554         var html = createHtml(o);
4555         return new Roo.Template(html);
4556     }
4557     };
4558 }();
4559 /*
4560  * Based on:
4561  * Ext JS Library 1.1.1
4562  * Copyright(c) 2006-2007, Ext JS, LLC.
4563  *
4564  * Originally Released Under LGPL - original licence link has changed is not relivant.
4565  *
4566  * Fork - LGPL
4567  * <script type="text/javascript">
4568  */
4569  
4570 /**
4571 * @class Roo.Template
4572 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4573 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4574 * Usage:
4575 <pre><code>
4576 var t = new Roo.Template({
4577     html :  '&lt;div name="{id}"&gt;' + 
4578         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4579         '&lt;/div&gt;',
4580     myformat: function (value, allValues) {
4581         return 'XX' + value;
4582     }
4583 });
4584 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4585 </code></pre>
4586 * For more information see this blog post with examples:
4587 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4588      - Create Elements using DOM, HTML fragments and Templates</a>. 
4589 * @constructor
4590 * @param {Object} cfg - Configuration object.
4591 */
4592 Roo.Template = function(cfg){
4593     // BC!
4594     if(cfg instanceof Array){
4595         cfg = cfg.join("");
4596     }else if(arguments.length > 1){
4597         cfg = Array.prototype.join.call(arguments, "");
4598     }
4599     
4600     
4601     if (typeof(cfg) == 'object') {
4602         Roo.apply(this,cfg)
4603     } else {
4604         // bc
4605         this.html = cfg;
4606     }
4607     if (this.url) {
4608         this.load();
4609     }
4610     
4611 };
4612 Roo.Template.prototype = {
4613     
4614     /**
4615      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4616      *                    it should be fixed so that template is observable...
4617      */
4618     url : false,
4619     /**
4620      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4621      */
4622     html : '',
4623     /**
4624      * Returns an HTML fragment of this template with the specified values applied.
4625      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4626      * @return {String} The HTML fragment
4627      */
4628     applyTemplate : function(values){
4629         try {
4630            
4631             if(this.compiled){
4632                 return this.compiled(values);
4633             }
4634             var useF = this.disableFormats !== true;
4635             var fm = Roo.util.Format, tpl = this;
4636             var fn = function(m, name, format, args){
4637                 if(format && useF){
4638                     if(format.substr(0, 5) == "this."){
4639                         return tpl.call(format.substr(5), values[name], values);
4640                     }else{
4641                         if(args){
4642                             // quoted values are required for strings in compiled templates, 
4643                             // but for non compiled we need to strip them
4644                             // quoted reversed for jsmin
4645                             var re = /^\s*['"](.*)["']\s*$/;
4646                             args = args.split(',');
4647                             for(var i = 0, len = args.length; i < len; i++){
4648                                 args[i] = args[i].replace(re, "$1");
4649                             }
4650                             args = [values[name]].concat(args);
4651                         }else{
4652                             args = [values[name]];
4653                         }
4654                         return fm[format].apply(fm, args);
4655                     }
4656                 }else{
4657                     return values[name] !== undefined ? values[name] : "";
4658                 }
4659             };
4660             return this.html.replace(this.re, fn);
4661         } catch (e) {
4662             Roo.log(e);
4663             throw e;
4664         }
4665          
4666     },
4667     
4668     loading : false,
4669       
4670     load : function ()
4671     {
4672          
4673         if (this.loading) {
4674             return;
4675         }
4676         var _t = this;
4677         
4678         this.loading = true;
4679         this.compiled = false;
4680         
4681         var cx = new Roo.data.Connection();
4682         cx.request({
4683             url : this.url,
4684             method : 'GET',
4685             success : function (response) {
4686                 _t.loading = false;
4687                 _t.html = response.responseText;
4688                 _t.url = false;
4689                 _t.compile();
4690              },
4691             failure : function(response) {
4692                 Roo.log("Template failed to load from " + _t.url);
4693                 _t.loading = false;
4694             }
4695         });
4696     },
4697
4698     /**
4699      * Sets the HTML used as the template and optionally compiles it.
4700      * @param {String} html
4701      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4702      * @return {Roo.Template} this
4703      */
4704     set : function(html, compile){
4705         this.html = html;
4706         this.compiled = null;
4707         if(compile){
4708             this.compile();
4709         }
4710         return this;
4711     },
4712     
4713     /**
4714      * True to disable format functions (defaults to false)
4715      * @type Boolean
4716      */
4717     disableFormats : false,
4718     
4719     /**
4720     * The regular expression used to match template variables 
4721     * @type RegExp
4722     * @property 
4723     */
4724     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4725     
4726     /**
4727      * Compiles the template into an internal function, eliminating the RegEx overhead.
4728      * @return {Roo.Template} this
4729      */
4730     compile : function(){
4731         var fm = Roo.util.Format;
4732         var useF = this.disableFormats !== true;
4733         var sep = Roo.isGecko ? "+" : ",";
4734         var fn = function(m, name, format, args){
4735             if(format && useF){
4736                 args = args ? ',' + args : "";
4737                 if(format.substr(0, 5) != "this."){
4738                     format = "fm." + format + '(';
4739                 }else{
4740                     format = 'this.call("'+ format.substr(5) + '", ';
4741                     args = ", values";
4742                 }
4743             }else{
4744                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4745             }
4746             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4747         };
4748         var body;
4749         // branched to use + in gecko and [].join() in others
4750         if(Roo.isGecko){
4751             body = "this.compiled = function(values){ return '" +
4752                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4753                     "';};";
4754         }else{
4755             body = ["this.compiled = function(values){ return ['"];
4756             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4757             body.push("'].join('');};");
4758             body = body.join('');
4759         }
4760         /**
4761          * eval:var:values
4762          * eval:var:fm
4763          */
4764         eval(body);
4765         return this;
4766     },
4767     
4768     // private function used to call members
4769     call : function(fnName, value, allValues){
4770         return this[fnName](value, allValues);
4771     },
4772     
4773     /**
4774      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4775      * @param {String/HTMLElement/Roo.Element} el The context element
4776      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4777      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4778      * @return {HTMLElement/Roo.Element} The new node or Element
4779      */
4780     insertFirst: function(el, values, returnElement){
4781         return this.doInsert('afterBegin', el, values, returnElement);
4782     },
4783
4784     /**
4785      * Applies the supplied values to the template and inserts the new node(s) before el.
4786      * @param {String/HTMLElement/Roo.Element} el The context element
4787      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4788      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4789      * @return {HTMLElement/Roo.Element} The new node or Element
4790      */
4791     insertBefore: function(el, values, returnElement){
4792         return this.doInsert('beforeBegin', el, values, returnElement);
4793     },
4794
4795     /**
4796      * Applies the supplied values to the template and inserts the new node(s) after el.
4797      * @param {String/HTMLElement/Roo.Element} el The context element
4798      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4799      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4800      * @return {HTMLElement/Roo.Element} The new node or Element
4801      */
4802     insertAfter : function(el, values, returnElement){
4803         return this.doInsert('afterEnd', el, values, returnElement);
4804     },
4805     
4806     /**
4807      * Applies the supplied values to the template and appends the new node(s) to el.
4808      * @param {String/HTMLElement/Roo.Element} el The context element
4809      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4810      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4811      * @return {HTMLElement/Roo.Element} The new node or Element
4812      */
4813     append : function(el, values, returnElement){
4814         return this.doInsert('beforeEnd', el, values, returnElement);
4815     },
4816
4817     doInsert : function(where, el, values, returnEl){
4818         el = Roo.getDom(el);
4819         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4820         return returnEl ? Roo.get(newNode, true) : newNode;
4821     },
4822
4823     /**
4824      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4825      * @param {String/HTMLElement/Roo.Element} el The context element
4826      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4827      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4828      * @return {HTMLElement/Roo.Element} The new node or Element
4829      */
4830     overwrite : function(el, values, returnElement){
4831         el = Roo.getDom(el);
4832         el.innerHTML = this.applyTemplate(values);
4833         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4834     }
4835 };
4836 /**
4837  * Alias for {@link #applyTemplate}
4838  * @method
4839  */
4840 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4841
4842 // backwards compat
4843 Roo.DomHelper.Template = Roo.Template;
4844
4845 /**
4846  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4847  * @param {String/HTMLElement} el A DOM element or its id
4848  * @returns {Roo.Template} The created template
4849  * @static
4850  */
4851 Roo.Template.from = function(el){
4852     el = Roo.getDom(el);
4853     return new Roo.Template(el.value || el.innerHTML);
4854 };/*
4855  * Based on:
4856  * Ext JS Library 1.1.1
4857  * Copyright(c) 2006-2007, Ext JS, LLC.
4858  *
4859  * Originally Released Under LGPL - original licence link has changed is not relivant.
4860  *
4861  * Fork - LGPL
4862  * <script type="text/javascript">
4863  */
4864  
4865
4866 /*
4867  * This is code is also distributed under MIT license for use
4868  * with jQuery and prototype JavaScript libraries.
4869  */
4870 /**
4871  * @class Roo.DomQuery
4872 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4873 <p>
4874 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4875
4876 <p>
4877 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4878 </p>
4879 <h4>Element Selectors:</h4>
4880 <ul class="list">
4881     <li> <b>*</b> any element</li>
4882     <li> <b>E</b> an element with the tag E</li>
4883     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4884     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4885     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4886     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4887 </ul>
4888 <h4>Attribute Selectors:</h4>
4889 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4890 <ul class="list">
4891     <li> <b>E[foo]</b> has an attribute "foo"</li>
4892     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4893     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4894     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4895     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4896     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4897     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4898 </ul>
4899 <h4>Pseudo Classes:</h4>
4900 <ul class="list">
4901     <li> <b>E:first-child</b> E is the first child of its parent</li>
4902     <li> <b>E:last-child</b> E is the last child of its parent</li>
4903     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4904     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4905     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4906     <li> <b>E:only-child</b> E is the only child of its parent</li>
4907     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4908     <li> <b>E:first</b> the first E in the resultset</li>
4909     <li> <b>E:last</b> the last E in the resultset</li>
4910     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4911     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4912     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4913     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4914     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4915     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4916     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4917     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4918     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4919 </ul>
4920 <h4>CSS Value Selectors:</h4>
4921 <ul class="list">
4922     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4923     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4924     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4925     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4926     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4927     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4928 </ul>
4929  * @singleton
4930  */
4931 Roo.DomQuery = function(){
4932     var cache = {}, simpleCache = {}, valueCache = {};
4933     var nonSpace = /\S/;
4934     var trimRe = /^\s+|\s+$/g;
4935     var tplRe = /\{(\d+)\}/g;
4936     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4937     var tagTokenRe = /^(#)?([\w-\*]+)/;
4938     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4939
4940     function child(p, index){
4941         var i = 0;
4942         var n = p.firstChild;
4943         while(n){
4944             if(n.nodeType == 1){
4945                if(++i == index){
4946                    return n;
4947                }
4948             }
4949             n = n.nextSibling;
4950         }
4951         return null;
4952     };
4953
4954     function next(n){
4955         while((n = n.nextSibling) && n.nodeType != 1);
4956         return n;
4957     };
4958
4959     function prev(n){
4960         while((n = n.previousSibling) && n.nodeType != 1);
4961         return n;
4962     };
4963
4964     function children(d){
4965         var n = d.firstChild, ni = -1;
4966             while(n){
4967                 var nx = n.nextSibling;
4968                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4969                     d.removeChild(n);
4970                 }else{
4971                     n.nodeIndex = ++ni;
4972                 }
4973                 n = nx;
4974             }
4975             return this;
4976         };
4977
4978     function byClassName(c, a, v){
4979         if(!v){
4980             return c;
4981         }
4982         var r = [], ri = -1, cn;
4983         for(var i = 0, ci; ci = c[i]; i++){
4984             if((' '+ci.className+' ').indexOf(v) != -1){
4985                 r[++ri] = ci;
4986             }
4987         }
4988         return r;
4989     };
4990
4991     function attrValue(n, attr){
4992         if(!n.tagName && typeof n.length != "undefined"){
4993             n = n[0];
4994         }
4995         if(!n){
4996             return null;
4997         }
4998         if(attr == "for"){
4999             return n.htmlFor;
5000         }
5001         if(attr == "class" || attr == "className"){
5002             return n.className;
5003         }
5004         return n.getAttribute(attr) || n[attr];
5005
5006     };
5007
5008     function getNodes(ns, mode, tagName){
5009         var result = [], ri = -1, cs;
5010         if(!ns){
5011             return result;
5012         }
5013         tagName = tagName || "*";
5014         if(typeof ns.getElementsByTagName != "undefined"){
5015             ns = [ns];
5016         }
5017         if(!mode){
5018             for(var i = 0, ni; ni = ns[i]; i++){
5019                 cs = ni.getElementsByTagName(tagName);
5020                 for(var j = 0, ci; ci = cs[j]; j++){
5021                     result[++ri] = ci;
5022                 }
5023             }
5024         }else if(mode == "/" || mode == ">"){
5025             var utag = tagName.toUpperCase();
5026             for(var i = 0, ni, cn; ni = ns[i]; i++){
5027                 cn = ni.children || ni.childNodes;
5028                 for(var j = 0, cj; cj = cn[j]; j++){
5029                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5030                         result[++ri] = cj;
5031                     }
5032                 }
5033             }
5034         }else if(mode == "+"){
5035             var utag = tagName.toUpperCase();
5036             for(var i = 0, n; n = ns[i]; i++){
5037                 while((n = n.nextSibling) && n.nodeType != 1);
5038                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5039                     result[++ri] = n;
5040                 }
5041             }
5042         }else if(mode == "~"){
5043             for(var i = 0, n; n = ns[i]; i++){
5044                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5045                 if(n){
5046                     result[++ri] = n;
5047                 }
5048             }
5049         }
5050         return result;
5051     };
5052
5053     function concat(a, b){
5054         if(b.slice){
5055             return a.concat(b);
5056         }
5057         for(var i = 0, l = b.length; i < l; i++){
5058             a[a.length] = b[i];
5059         }
5060         return a;
5061     }
5062
5063     function byTag(cs, tagName){
5064         if(cs.tagName || cs == document){
5065             cs = [cs];
5066         }
5067         if(!tagName){
5068             return cs;
5069         }
5070         var r = [], ri = -1;
5071         tagName = tagName.toLowerCase();
5072         for(var i = 0, ci; ci = cs[i]; i++){
5073             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5074                 r[++ri] = ci;
5075             }
5076         }
5077         return r;
5078     };
5079
5080     function byId(cs, attr, id){
5081         if(cs.tagName || cs == document){
5082             cs = [cs];
5083         }
5084         if(!id){
5085             return cs;
5086         }
5087         var r = [], ri = -1;
5088         for(var i = 0,ci; ci = cs[i]; i++){
5089             if(ci && ci.id == id){
5090                 r[++ri] = ci;
5091                 return r;
5092             }
5093         }
5094         return r;
5095     };
5096
5097     function byAttribute(cs, attr, value, op, custom){
5098         var r = [], ri = -1, st = custom=="{";
5099         var f = Roo.DomQuery.operators[op];
5100         for(var i = 0, ci; ci = cs[i]; i++){
5101             var a;
5102             if(st){
5103                 a = Roo.DomQuery.getStyle(ci, attr);
5104             }
5105             else if(attr == "class" || attr == "className"){
5106                 a = ci.className;
5107             }else if(attr == "for"){
5108                 a = ci.htmlFor;
5109             }else if(attr == "href"){
5110                 a = ci.getAttribute("href", 2);
5111             }else{
5112                 a = ci.getAttribute(attr);
5113             }
5114             if((f && f(a, value)) || (!f && a)){
5115                 r[++ri] = ci;
5116             }
5117         }
5118         return r;
5119     };
5120
5121     function byPseudo(cs, name, value){
5122         return Roo.DomQuery.pseudos[name](cs, value);
5123     };
5124
5125     // This is for IE MSXML which does not support expandos.
5126     // IE runs the same speed using setAttribute, however FF slows way down
5127     // and Safari completely fails so they need to continue to use expandos.
5128     var isIE = window.ActiveXObject ? true : false;
5129
5130     // this eval is stop the compressor from
5131     // renaming the variable to something shorter
5132     
5133     /** eval:var:batch */
5134     var batch = 30803; 
5135
5136     var key = 30803;
5137
5138     function nodupIEXml(cs){
5139         var d = ++key;
5140         cs[0].setAttribute("_nodup", d);
5141         var r = [cs[0]];
5142         for(var i = 1, len = cs.length; i < len; i++){
5143             var c = cs[i];
5144             if(!c.getAttribute("_nodup") != d){
5145                 c.setAttribute("_nodup", d);
5146                 r[r.length] = c;
5147             }
5148         }
5149         for(var i = 0, len = cs.length; i < len; i++){
5150             cs[i].removeAttribute("_nodup");
5151         }
5152         return r;
5153     }
5154
5155     function nodup(cs){
5156         if(!cs){
5157             return [];
5158         }
5159         var len = cs.length, c, i, r = cs, cj, ri = -1;
5160         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5161             return cs;
5162         }
5163         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5164             return nodupIEXml(cs);
5165         }
5166         var d = ++key;
5167         cs[0]._nodup = d;
5168         for(i = 1; c = cs[i]; i++){
5169             if(c._nodup != d){
5170                 c._nodup = d;
5171             }else{
5172                 r = [];
5173                 for(var j = 0; j < i; j++){
5174                     r[++ri] = cs[j];
5175                 }
5176                 for(j = i+1; cj = cs[j]; j++){
5177                     if(cj._nodup != d){
5178                         cj._nodup = d;
5179                         r[++ri] = cj;
5180                     }
5181                 }
5182                 return r;
5183             }
5184         }
5185         return r;
5186     }
5187
5188     function quickDiffIEXml(c1, c2){
5189         var d = ++key;
5190         for(var i = 0, len = c1.length; i < len; i++){
5191             c1[i].setAttribute("_qdiff", d);
5192         }
5193         var r = [];
5194         for(var i = 0, len = c2.length; i < len; i++){
5195             if(c2[i].getAttribute("_qdiff") != d){
5196                 r[r.length] = c2[i];
5197             }
5198         }
5199         for(var i = 0, len = c1.length; i < len; i++){
5200            c1[i].removeAttribute("_qdiff");
5201         }
5202         return r;
5203     }
5204
5205     function quickDiff(c1, c2){
5206         var len1 = c1.length;
5207         if(!len1){
5208             return c2;
5209         }
5210         if(isIE && c1[0].selectSingleNode){
5211             return quickDiffIEXml(c1, c2);
5212         }
5213         var d = ++key;
5214         for(var i = 0; i < len1; i++){
5215             c1[i]._qdiff = d;
5216         }
5217         var r = [];
5218         for(var i = 0, len = c2.length; i < len; i++){
5219             if(c2[i]._qdiff != d){
5220                 r[r.length] = c2[i];
5221             }
5222         }
5223         return r;
5224     }
5225
5226     function quickId(ns, mode, root, id){
5227         if(ns == root){
5228            var d = root.ownerDocument || root;
5229            return d.getElementById(id);
5230         }
5231         ns = getNodes(ns, mode, "*");
5232         return byId(ns, null, id);
5233     }
5234
5235     return {
5236         getStyle : function(el, name){
5237             return Roo.fly(el).getStyle(name);
5238         },
5239         /**
5240          * Compiles a selector/xpath query into a reusable function. The returned function
5241          * takes one parameter "root" (optional), which is the context node from where the query should start.
5242          * @param {String} selector The selector/xpath query
5243          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5244          * @return {Function}
5245          */
5246         compile : function(path, type){
5247             type = type || "select";
5248             
5249             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5250             var q = path, mode, lq;
5251             var tk = Roo.DomQuery.matchers;
5252             var tklen = tk.length;
5253             var mm;
5254
5255             // accept leading mode switch
5256             var lmode = q.match(modeRe);
5257             if(lmode && lmode[1]){
5258                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5259                 q = q.replace(lmode[1], "");
5260             }
5261             // strip leading slashes
5262             while(path.substr(0, 1)=="/"){
5263                 path = path.substr(1);
5264             }
5265
5266             while(q && lq != q){
5267                 lq = q;
5268                 var tm = q.match(tagTokenRe);
5269                 if(type == "select"){
5270                     if(tm){
5271                         if(tm[1] == "#"){
5272                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5273                         }else{
5274                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5275                         }
5276                         q = q.replace(tm[0], "");
5277                     }else if(q.substr(0, 1) != '@'){
5278                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5279                     }
5280                 }else{
5281                     if(tm){
5282                         if(tm[1] == "#"){
5283                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5284                         }else{
5285                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5286                         }
5287                         q = q.replace(tm[0], "");
5288                     }
5289                 }
5290                 while(!(mm = q.match(modeRe))){
5291                     var matched = false;
5292                     for(var j = 0; j < tklen; j++){
5293                         var t = tk[j];
5294                         var m = q.match(t.re);
5295                         if(m){
5296                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5297                                                     return m[i];
5298                                                 });
5299                             q = q.replace(m[0], "");
5300                             matched = true;
5301                             break;
5302                         }
5303                     }
5304                     // prevent infinite loop on bad selector
5305                     if(!matched){
5306                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5307                     }
5308                 }
5309                 if(mm[1]){
5310                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5311                     q = q.replace(mm[1], "");
5312                 }
5313             }
5314             fn[fn.length] = "return nodup(n);\n}";
5315             
5316              /** 
5317               * list of variables that need from compression as they are used by eval.
5318              *  eval:var:batch 
5319              *  eval:var:nodup
5320              *  eval:var:byTag
5321              *  eval:var:ById
5322              *  eval:var:getNodes
5323              *  eval:var:quickId
5324              *  eval:var:mode
5325              *  eval:var:root
5326              *  eval:var:n
5327              *  eval:var:byClassName
5328              *  eval:var:byPseudo
5329              *  eval:var:byAttribute
5330              *  eval:var:attrValue
5331              * 
5332              **/ 
5333             eval(fn.join(""));
5334             return f;
5335         },
5336
5337         /**
5338          * Selects a group of elements.
5339          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5340          * @param {Node} root (optional) The start of the query (defaults to document).
5341          * @return {Array}
5342          */
5343         select : function(path, root, type){
5344             if(!root || root == document){
5345                 root = document;
5346             }
5347             if(typeof root == "string"){
5348                 root = document.getElementById(root);
5349             }
5350             var paths = path.split(",");
5351             var results = [];
5352             for(var i = 0, len = paths.length; i < len; i++){
5353                 var p = paths[i].replace(trimRe, "");
5354                 if(!cache[p]){
5355                     cache[p] = Roo.DomQuery.compile(p);
5356                     if(!cache[p]){
5357                         throw p + " is not a valid selector";
5358                     }
5359                 }
5360                 var result = cache[p](root);
5361                 if(result && result != document){
5362                     results = results.concat(result);
5363                 }
5364             }
5365             if(paths.length > 1){
5366                 return nodup(results);
5367             }
5368             return results;
5369         },
5370
5371         /**
5372          * Selects a single element.
5373          * @param {String} selector The selector/xpath query
5374          * @param {Node} root (optional) The start of the query (defaults to document).
5375          * @return {Element}
5376          */
5377         selectNode : function(path, root){
5378             return Roo.DomQuery.select(path, root)[0];
5379         },
5380
5381         /**
5382          * Selects the value of a node, optionally replacing null with the defaultValue.
5383          * @param {String} selector The selector/xpath query
5384          * @param {Node} root (optional) The start of the query (defaults to document).
5385          * @param {String} defaultValue
5386          */
5387         selectValue : function(path, root, defaultValue){
5388             path = path.replace(trimRe, "");
5389             if(!valueCache[path]){
5390                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5391             }
5392             var n = valueCache[path](root);
5393             n = n[0] ? n[0] : n;
5394             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5395             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5396         },
5397
5398         /**
5399          * Selects the value of a node, parsing integers and floats.
5400          * @param {String} selector The selector/xpath query
5401          * @param {Node} root (optional) The start of the query (defaults to document).
5402          * @param {Number} defaultValue
5403          * @return {Number}
5404          */
5405         selectNumber : function(path, root, defaultValue){
5406             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5407             return parseFloat(v);
5408         },
5409
5410         /**
5411          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5412          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5413          * @param {String} selector The simple selector to test
5414          * @return {Boolean}
5415          */
5416         is : function(el, ss){
5417             if(typeof el == "string"){
5418                 el = document.getElementById(el);
5419             }
5420             var isArray = (el instanceof Array);
5421             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5422             return isArray ? (result.length == el.length) : (result.length > 0);
5423         },
5424
5425         /**
5426          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5427          * @param {Array} el An array of elements to filter
5428          * @param {String} selector The simple selector to test
5429          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5430          * the selector instead of the ones that match
5431          * @return {Array}
5432          */
5433         filter : function(els, ss, nonMatches){
5434             ss = ss.replace(trimRe, "");
5435             if(!simpleCache[ss]){
5436                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5437             }
5438             var result = simpleCache[ss](els);
5439             return nonMatches ? quickDiff(result, els) : result;
5440         },
5441
5442         /**
5443          * Collection of matching regular expressions and code snippets.
5444          */
5445         matchers : [{
5446                 re: /^\.([\w-]+)/,
5447                 select: 'n = byClassName(n, null, " {1} ");'
5448             }, {
5449                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5450                 select: 'n = byPseudo(n, "{1}", "{2}");'
5451             },{
5452                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5453                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5454             }, {
5455                 re: /^#([\w-]+)/,
5456                 select: 'n = byId(n, null, "{1}");'
5457             },{
5458                 re: /^@([\w-]+)/,
5459                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5460             }
5461         ],
5462
5463         /**
5464          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5465          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5466          */
5467         operators : {
5468             "=" : function(a, v){
5469                 return a == v;
5470             },
5471             "!=" : function(a, v){
5472                 return a != v;
5473             },
5474             "^=" : function(a, v){
5475                 return a && a.substr(0, v.length) == v;
5476             },
5477             "$=" : function(a, v){
5478                 return a && a.substr(a.length-v.length) == v;
5479             },
5480             "*=" : function(a, v){
5481                 return a && a.indexOf(v) !== -1;
5482             },
5483             "%=" : function(a, v){
5484                 return (a % v) == 0;
5485             },
5486             "|=" : function(a, v){
5487                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5488             },
5489             "~=" : function(a, v){
5490                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5491             }
5492         },
5493
5494         /**
5495          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5496          * and the argument (if any) supplied in the selector.
5497          */
5498         pseudos : {
5499             "first-child" : function(c){
5500                 var r = [], ri = -1, n;
5501                 for(var i = 0, ci; ci = n = c[i]; i++){
5502                     while((n = n.previousSibling) && n.nodeType != 1);
5503                     if(!n){
5504                         r[++ri] = ci;
5505                     }
5506                 }
5507                 return r;
5508             },
5509
5510             "last-child" : function(c){
5511                 var r = [], ri = -1, n;
5512                 for(var i = 0, ci; ci = n = c[i]; i++){
5513                     while((n = n.nextSibling) && n.nodeType != 1);
5514                     if(!n){
5515                         r[++ri] = ci;
5516                     }
5517                 }
5518                 return r;
5519             },
5520
5521             "nth-child" : function(c, a) {
5522                 var r = [], ri = -1;
5523                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5524                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5525                 for(var i = 0, n; n = c[i]; i++){
5526                     var pn = n.parentNode;
5527                     if (batch != pn._batch) {
5528                         var j = 0;
5529                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5530                             if(cn.nodeType == 1){
5531                                cn.nodeIndex = ++j;
5532                             }
5533                         }
5534                         pn._batch = batch;
5535                     }
5536                     if (f == 1) {
5537                         if (l == 0 || n.nodeIndex == l){
5538                             r[++ri] = n;
5539                         }
5540                     } else if ((n.nodeIndex + l) % f == 0){
5541                         r[++ri] = n;
5542                     }
5543                 }
5544
5545                 return r;
5546             },
5547
5548             "only-child" : function(c){
5549                 var r = [], ri = -1;;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     if(!prev(ci) && !next(ci)){
5552                         r[++ri] = ci;
5553                     }
5554                 }
5555                 return r;
5556             },
5557
5558             "empty" : function(c){
5559                 var r = [], ri = -1;
5560                 for(var i = 0, ci; ci = c[i]; i++){
5561                     var cns = ci.childNodes, j = 0, cn, empty = true;
5562                     while(cn = cns[j]){
5563                         ++j;
5564                         if(cn.nodeType == 1 || cn.nodeType == 3){
5565                             empty = false;
5566                             break;
5567                         }
5568                     }
5569                     if(empty){
5570                         r[++ri] = ci;
5571                     }
5572                 }
5573                 return r;
5574             },
5575
5576             "contains" : function(c, v){
5577                 var r = [], ri = -1;
5578                 for(var i = 0, ci; ci = c[i]; i++){
5579                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5580                         r[++ri] = ci;
5581                     }
5582                 }
5583                 return r;
5584             },
5585
5586             "nodeValue" : function(c, v){
5587                 var r = [], ri = -1;
5588                 for(var i = 0, ci; ci = c[i]; i++){
5589                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5590                         r[++ri] = ci;
5591                     }
5592                 }
5593                 return r;
5594             },
5595
5596             "checked" : function(c){
5597                 var r = [], ri = -1;
5598                 for(var i = 0, ci; ci = c[i]; i++){
5599                     if(ci.checked == true){
5600                         r[++ri] = ci;
5601                     }
5602                 }
5603                 return r;
5604             },
5605
5606             "not" : function(c, ss){
5607                 return Roo.DomQuery.filter(c, ss, true);
5608             },
5609
5610             "odd" : function(c){
5611                 return this["nth-child"](c, "odd");
5612             },
5613
5614             "even" : function(c){
5615                 return this["nth-child"](c, "even");
5616             },
5617
5618             "nth" : function(c, a){
5619                 return c[a-1] || [];
5620             },
5621
5622             "first" : function(c){
5623                 return c[0] || [];
5624             },
5625
5626             "last" : function(c){
5627                 return c[c.length-1] || [];
5628             },
5629
5630             "has" : function(c, ss){
5631                 var s = Roo.DomQuery.select;
5632                 var r = [], ri = -1;
5633                 for(var i = 0, ci; ci = c[i]; i++){
5634                     if(s(ss, ci).length > 0){
5635                         r[++ri] = ci;
5636                     }
5637                 }
5638                 return r;
5639             },
5640
5641             "next" : function(c, ss){
5642                 var is = Roo.DomQuery.is;
5643                 var r = [], ri = -1;
5644                 for(var i = 0, ci; ci = c[i]; i++){
5645                     var n = next(ci);
5646                     if(n && is(n, ss)){
5647                         r[++ri] = ci;
5648                     }
5649                 }
5650                 return r;
5651             },
5652
5653             "prev" : function(c, ss){
5654                 var is = Roo.DomQuery.is;
5655                 var r = [], ri = -1;
5656                 for(var i = 0, ci; ci = c[i]; i++){
5657                     var n = prev(ci);
5658                     if(n && is(n, ss)){
5659                         r[++ri] = ci;
5660                     }
5661                 }
5662                 return r;
5663             }
5664         }
5665     };
5666 }();
5667
5668 /**
5669  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5670  * @param {String} path The selector/xpath query
5671  * @param {Node} root (optional) The start of the query (defaults to document).
5672  * @return {Array}
5673  * @member Roo
5674  * @method query
5675  */
5676 Roo.query = Roo.DomQuery.select;
5677 /*
5678  * Based on:
5679  * Ext JS Library 1.1.1
5680  * Copyright(c) 2006-2007, Ext JS, LLC.
5681  *
5682  * Originally Released Under LGPL - original licence link has changed is not relivant.
5683  *
5684  * Fork - LGPL
5685  * <script type="text/javascript">
5686  */
5687
5688 /**
5689  * @class Roo.util.Observable
5690  * Base class that provides a common interface for publishing events. Subclasses are expected to
5691  * to have a property "events" with all the events defined.<br>
5692  * For example:
5693  * <pre><code>
5694  Employee = function(name){
5695     this.name = name;
5696     this.addEvents({
5697         "fired" : true,
5698         "quit" : true
5699     });
5700  }
5701  Roo.extend(Employee, Roo.util.Observable);
5702 </code></pre>
5703  * @param {Object} config properties to use (incuding events / listeners)
5704  */
5705
5706 Roo.util.Observable = function(cfg){
5707     
5708     cfg = cfg|| {};
5709     this.addEvents(cfg.events || {});
5710     if (cfg.events) {
5711         delete cfg.events; // make sure
5712     }
5713      
5714     Roo.apply(this, cfg);
5715     
5716     if(this.listeners){
5717         this.on(this.listeners);
5718         delete this.listeners;
5719     }
5720 };
5721 Roo.util.Observable.prototype = {
5722     /** 
5723  * @cfg {Object} listeners  list of events and functions to call for this object, 
5724  * For example :
5725  * <pre><code>
5726     listeners :  { 
5727        'click' : function(e) {
5728            ..... 
5729         } ,
5730         .... 
5731     } 
5732   </code></pre>
5733  */
5734     
5735     
5736     /**
5737      * Fires the specified event with the passed parameters (minus the event name).
5738      * @param {String} eventName
5739      * @param {Object...} args Variable number of parameters are passed to handlers
5740      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5741      */
5742     fireEvent : function(){
5743         var ce = this.events[arguments[0].toLowerCase()];
5744         if(typeof ce == "object"){
5745             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5746         }else{
5747             return true;
5748         }
5749     },
5750
5751     // private
5752     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5753
5754     /**
5755      * Appends an event handler to this component
5756      * @param {String}   eventName The type of event to listen for
5757      * @param {Function} handler The method the event invokes
5758      * @param {Object}   scope (optional) The scope in which to execute the handler
5759      * function. The handler function's "this" context.
5760      * @param {Object}   options (optional) An object containing handler configuration
5761      * properties. This may contain any of the following properties:<ul>
5762      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5763      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5764      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5765      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5766      * by the specified number of milliseconds. If the event fires again within that time, the original
5767      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5768      * </ul><br>
5769      * <p>
5770      * <b>Combining Options</b><br>
5771      * Using the options argument, it is possible to combine different types of listeners:<br>
5772      * <br>
5773      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5774                 <pre><code>
5775                 el.on('click', this.onClick, this, {
5776                         single: true,
5777                 delay: 100,
5778                 forumId: 4
5779                 });
5780                 </code></pre>
5781      * <p>
5782      * <b>Attaching multiple handlers in 1 call</b><br>
5783      * The method also allows for a single argument to be passed which is a config object containing properties
5784      * which specify multiple handlers.
5785      * <pre><code>
5786                 el.on({
5787                         'click': {
5788                         fn: this.onClick,
5789                         scope: this,
5790                         delay: 100
5791                 }, 
5792                 'mouseover': {
5793                         fn: this.onMouseOver,
5794                         scope: this
5795                 },
5796                 'mouseout': {
5797                         fn: this.onMouseOut,
5798                         scope: this
5799                 }
5800                 });
5801                 </code></pre>
5802      * <p>
5803      * Or a shorthand syntax which passes the same scope object to all handlers:
5804         <pre><code>
5805                 el.on({
5806                         'click': this.onClick,
5807                 'mouseover': this.onMouseOver,
5808                 'mouseout': this.onMouseOut,
5809                 scope: this
5810                 });
5811                 </code></pre>
5812      */
5813     addListener : function(eventName, fn, scope, o){
5814         if(typeof eventName == "object"){
5815             o = eventName;
5816             for(var e in o){
5817                 if(this.filterOptRe.test(e)){
5818                     continue;
5819                 }
5820                 if(typeof o[e] == "function"){
5821                     // shared options
5822                     this.addListener(e, o[e], o.scope,  o);
5823                 }else{
5824                     // individual options
5825                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5826                 }
5827             }
5828             return;
5829         }
5830         o = (!o || typeof o == "boolean") ? {} : o;
5831         eventName = eventName.toLowerCase();
5832         var ce = this.events[eventName] || true;
5833         if(typeof ce == "boolean"){
5834             ce = new Roo.util.Event(this, eventName);
5835             this.events[eventName] = ce;
5836         }
5837         ce.addListener(fn, scope, o);
5838     },
5839
5840     /**
5841      * Removes a listener
5842      * @param {String}   eventName     The type of event to listen for
5843      * @param {Function} handler        The handler to remove
5844      * @param {Object}   scope  (optional) The scope (this object) for the handler
5845      */
5846     removeListener : function(eventName, fn, scope){
5847         var ce = this.events[eventName.toLowerCase()];
5848         if(typeof ce == "object"){
5849             ce.removeListener(fn, scope);
5850         }
5851     },
5852
5853     /**
5854      * Removes all listeners for this object
5855      */
5856     purgeListeners : function(){
5857         for(var evt in this.events){
5858             if(typeof this.events[evt] == "object"){
5859                  this.events[evt].clearListeners();
5860             }
5861         }
5862     },
5863
5864     relayEvents : function(o, events){
5865         var createHandler = function(ename){
5866             return function(){
5867                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5868             };
5869         };
5870         for(var i = 0, len = events.length; i < len; i++){
5871             var ename = events[i];
5872             if(!this.events[ename]){ this.events[ename] = true; };
5873             o.on(ename, createHandler(ename), this);
5874         }
5875     },
5876
5877     /**
5878      * Used to define events on this Observable
5879      * @param {Object} object The object with the events defined
5880      */
5881     addEvents : function(o){
5882         if(!this.events){
5883             this.events = {};
5884         }
5885         Roo.applyIf(this.events, o);
5886     },
5887
5888     /**
5889      * Checks to see if this object has any listeners for a specified event
5890      * @param {String} eventName The name of the event to check for
5891      * @return {Boolean} True if the event is being listened for, else false
5892      */
5893     hasListener : function(eventName){
5894         var e = this.events[eventName];
5895         return typeof e == "object" && e.listeners.length > 0;
5896     }
5897 };
5898 /**
5899  * Appends an event handler to this element (shorthand for addListener)
5900  * @param {String}   eventName     The type of event to listen for
5901  * @param {Function} handler        The method the event invokes
5902  * @param {Object}   scope (optional) The scope in which to execute the handler
5903  * function. The handler function's "this" context.
5904  * @param {Object}   options  (optional)
5905  * @method
5906  */
5907 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5908 /**
5909  * Removes a listener (shorthand for removeListener)
5910  * @param {String}   eventName     The type of event to listen for
5911  * @param {Function} handler        The handler to remove
5912  * @param {Object}   scope  (optional) The scope (this object) for the handler
5913  * @method
5914  */
5915 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5916
5917 /**
5918  * Starts capture on the specified Observable. All events will be passed
5919  * to the supplied function with the event name + standard signature of the event
5920  * <b>before</b> the event is fired. If the supplied function returns false,
5921  * the event will not fire.
5922  * @param {Observable} o The Observable to capture
5923  * @param {Function} fn The function to call
5924  * @param {Object} scope (optional) The scope (this object) for the fn
5925  * @static
5926  */
5927 Roo.util.Observable.capture = function(o, fn, scope){
5928     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5929 };
5930
5931 /**
5932  * Removes <b>all</b> added captures from the Observable.
5933  * @param {Observable} o The Observable to release
5934  * @static
5935  */
5936 Roo.util.Observable.releaseCapture = function(o){
5937     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5938 };
5939
5940 (function(){
5941
5942     var createBuffered = function(h, o, scope){
5943         var task = new Roo.util.DelayedTask();
5944         return function(){
5945             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5946         };
5947     };
5948
5949     var createSingle = function(h, e, fn, scope){
5950         return function(){
5951             e.removeListener(fn, scope);
5952             return h.apply(scope, arguments);
5953         };
5954     };
5955
5956     var createDelayed = function(h, o, scope){
5957         return function(){
5958             var args = Array.prototype.slice.call(arguments, 0);
5959             setTimeout(function(){
5960                 h.apply(scope, args);
5961             }, o.delay || 10);
5962         };
5963     };
5964
5965     Roo.util.Event = function(obj, name){
5966         this.name = name;
5967         this.obj = obj;
5968         this.listeners = [];
5969     };
5970
5971     Roo.util.Event.prototype = {
5972         addListener : function(fn, scope, options){
5973             var o = options || {};
5974             scope = scope || this.obj;
5975             if(!this.isListening(fn, scope)){
5976                 var l = {fn: fn, scope: scope, options: o};
5977                 var h = fn;
5978                 if(o.delay){
5979                     h = createDelayed(h, o, scope);
5980                 }
5981                 if(o.single){
5982                     h = createSingle(h, this, fn, scope);
5983                 }
5984                 if(o.buffer){
5985                     h = createBuffered(h, o, scope);
5986                 }
5987                 l.fireFn = h;
5988                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5989                     this.listeners.push(l);
5990                 }else{
5991                     this.listeners = this.listeners.slice(0);
5992                     this.listeners.push(l);
5993                 }
5994             }
5995         },
5996
5997         findListener : function(fn, scope){
5998             scope = scope || this.obj;
5999             var ls = this.listeners;
6000             for(var i = 0, len = ls.length; i < len; i++){
6001                 var l = ls[i];
6002                 if(l.fn == fn && l.scope == scope){
6003                     return i;
6004                 }
6005             }
6006             return -1;
6007         },
6008
6009         isListening : function(fn, scope){
6010             return this.findListener(fn, scope) != -1;
6011         },
6012
6013         removeListener : function(fn, scope){
6014             var index;
6015             if((index = this.findListener(fn, scope)) != -1){
6016                 if(!this.firing){
6017                     this.listeners.splice(index, 1);
6018                 }else{
6019                     this.listeners = this.listeners.slice(0);
6020                     this.listeners.splice(index, 1);
6021                 }
6022                 return true;
6023             }
6024             return false;
6025         },
6026
6027         clearListeners : function(){
6028             this.listeners = [];
6029         },
6030
6031         fire : function(){
6032             var ls = this.listeners, scope, len = ls.length;
6033             if(len > 0){
6034                 this.firing = true;
6035                 var args = Array.prototype.slice.call(arguments, 0);
6036                 for(var i = 0; i < len; i++){
6037                     var l = ls[i];
6038                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6039                         this.firing = false;
6040                         return false;
6041                     }
6042                 }
6043                 this.firing = false;
6044             }
6045             return true;
6046         }
6047     };
6048 })();/*
6049  * Based on:
6050  * Ext JS Library 1.1.1
6051  * Copyright(c) 2006-2007, Ext JS, LLC.
6052  *
6053  * Originally Released Under LGPL - original licence link has changed is not relivant.
6054  *
6055  * Fork - LGPL
6056  * <script type="text/javascript">
6057  */
6058
6059 /**
6060  * @class Roo.EventManager
6061  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6062  * several useful events directly.
6063  * See {@link Roo.EventObject} for more details on normalized event objects.
6064  * @singleton
6065  */
6066 Roo.EventManager = function(){
6067     var docReadyEvent, docReadyProcId, docReadyState = false;
6068     var resizeEvent, resizeTask, textEvent, textSize;
6069     var E = Roo.lib.Event;
6070     var D = Roo.lib.Dom;
6071
6072     
6073     
6074
6075     var fireDocReady = function(){
6076         if(!docReadyState){
6077             docReadyState = true;
6078             Roo.isReady = true;
6079             if(docReadyProcId){
6080                 clearInterval(docReadyProcId);
6081             }
6082             if(Roo.isGecko || Roo.isOpera) {
6083                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6084             }
6085             if(Roo.isIE){
6086                 var defer = document.getElementById("ie-deferred-loader");
6087                 if(defer){
6088                     defer.onreadystatechange = null;
6089                     defer.parentNode.removeChild(defer);
6090                 }
6091             }
6092             if(docReadyEvent){
6093                 docReadyEvent.fire();
6094                 docReadyEvent.clearListeners();
6095             }
6096         }
6097     };
6098     
6099     var initDocReady = function(){
6100         docReadyEvent = new Roo.util.Event();
6101         if(Roo.isGecko || Roo.isOpera) {
6102             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6103         }else if(Roo.isIE){
6104             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6105             var defer = document.getElementById("ie-deferred-loader");
6106             defer.onreadystatechange = function(){
6107                 if(this.readyState == "complete"){
6108                     fireDocReady();
6109                 }
6110             };
6111         }else if(Roo.isSafari){ 
6112             docReadyProcId = setInterval(function(){
6113                 var rs = document.readyState;
6114                 if(rs == "complete") {
6115                     fireDocReady();     
6116                  }
6117             }, 10);
6118         }
6119         // no matter what, make sure it fires on load
6120         E.on(window, "load", fireDocReady);
6121     };
6122
6123     var createBuffered = function(h, o){
6124         var task = new Roo.util.DelayedTask(h);
6125         return function(e){
6126             // create new event object impl so new events don't wipe out properties
6127             e = new Roo.EventObjectImpl(e);
6128             task.delay(o.buffer, h, null, [e]);
6129         };
6130     };
6131
6132     var createSingle = function(h, el, ename, fn){
6133         return function(e){
6134             Roo.EventManager.removeListener(el, ename, fn);
6135             h(e);
6136         };
6137     };
6138
6139     var createDelayed = function(h, o){
6140         return function(e){
6141             // create new event object impl so new events don't wipe out properties
6142             e = new Roo.EventObjectImpl(e);
6143             setTimeout(function(){
6144                 h(e);
6145             }, o.delay || 10);
6146         };
6147     };
6148     var transitionEndVal = false;
6149     
6150     var transitionEnd = function()
6151     {
6152         if (transitionEndVal) {
6153             return transitionEndVal;
6154         }
6155         var el = document.createElement('div');
6156
6157         var transEndEventNames = {
6158             WebkitTransition : 'webkitTransitionEnd',
6159             MozTransition    : 'transitionend',
6160             OTransition      : 'oTransitionEnd otransitionend',
6161             transition       : 'transitionend'
6162         };
6163     
6164         for (var name in transEndEventNames) {
6165             if (el.style[name] !== undefined) {
6166                 transitionEndVal = transEndEventNames[name];
6167                 return  transitionEndVal ;
6168             }
6169         }
6170     }
6171     
6172
6173     var listen = function(element, ename, opt, fn, scope){
6174         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6175         fn = fn || o.fn; scope = scope || o.scope;
6176         var el = Roo.getDom(element);
6177         
6178         
6179         if(!el){
6180             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6181         }
6182         
6183         if (ename == 'transitionend') {
6184             ename = transitionEnd();
6185         }
6186         var h = function(e){
6187             e = Roo.EventObject.setEvent(e);
6188             var t;
6189             if(o.delegate){
6190                 t = e.getTarget(o.delegate, el);
6191                 if(!t){
6192                     return;
6193                 }
6194             }else{
6195                 t = e.target;
6196             }
6197             if(o.stopEvent === true){
6198                 e.stopEvent();
6199             }
6200             if(o.preventDefault === true){
6201                e.preventDefault();
6202             }
6203             if(o.stopPropagation === true){
6204                 e.stopPropagation();
6205             }
6206
6207             if(o.normalized === false){
6208                 e = e.browserEvent;
6209             }
6210
6211             fn.call(scope || el, e, t, o);
6212         };
6213         if(o.delay){
6214             h = createDelayed(h, o);
6215         }
6216         if(o.single){
6217             h = createSingle(h, el, ename, fn);
6218         }
6219         if(o.buffer){
6220             h = createBuffered(h, o);
6221         }
6222         fn._handlers = fn._handlers || [];
6223         
6224         
6225         fn._handlers.push([Roo.id(el), ename, h]);
6226         
6227         
6228          
6229         E.on(el, ename, h);
6230         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6231             el.addEventListener("DOMMouseScroll", h, false);
6232             E.on(window, 'unload', function(){
6233                 el.removeEventListener("DOMMouseScroll", h, false);
6234             });
6235         }
6236         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6237             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6238         }
6239         return h;
6240     };
6241
6242     var stopListening = function(el, ename, fn){
6243         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6244         if(hds){
6245             for(var i = 0, len = hds.length; i < len; i++){
6246                 var h = hds[i];
6247                 if(h[0] == id && h[1] == ename){
6248                     hd = h[2];
6249                     hds.splice(i, 1);
6250                     break;
6251                 }
6252             }
6253         }
6254         E.un(el, ename, hd);
6255         el = Roo.getDom(el);
6256         if(ename == "mousewheel" && el.addEventListener){
6257             el.removeEventListener("DOMMouseScroll", hd, false);
6258         }
6259         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6260             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6261         }
6262     };
6263
6264     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6265     
6266     var pub = {
6267         
6268         
6269         /** 
6270          * Fix for doc tools
6271          * @scope Roo.EventManager
6272          */
6273         
6274         
6275         /** 
6276          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6277          * object with a Roo.EventObject
6278          * @param {Function} fn        The method the event invokes
6279          * @param {Object}   scope    An object that becomes the scope of the handler
6280          * @param {boolean}  override If true, the obj passed in becomes
6281          *                             the execution scope of the listener
6282          * @return {Function} The wrapped function
6283          * @deprecated
6284          */
6285         wrap : function(fn, scope, override){
6286             return function(e){
6287                 Roo.EventObject.setEvent(e);
6288                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6289             };
6290         },
6291         
6292         /**
6293      * Appends an event handler to an element (shorthand for addListener)
6294      * @param {String/HTMLElement}   element        The html element or id to assign the
6295      * @param {String}   eventName The type of event to listen for
6296      * @param {Function} handler The method the event invokes
6297      * @param {Object}   scope (optional) The scope in which to execute the handler
6298      * function. The handler function's "this" context.
6299      * @param {Object}   options (optional) An object containing handler configuration
6300      * properties. This may contain any of the following properties:<ul>
6301      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6302      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6303      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6304      * <li>preventDefault {Boolean} True to prevent the default action</li>
6305      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6306      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6307      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6308      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6309      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6310      * by the specified number of milliseconds. If the event fires again within that time, the original
6311      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6312      * </ul><br>
6313      * <p>
6314      * <b>Combining Options</b><br>
6315      * Using the options argument, it is possible to combine different types of listeners:<br>
6316      * <br>
6317      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6318      * Code:<pre><code>
6319 el.on('click', this.onClick, this, {
6320     single: true,
6321     delay: 100,
6322     stopEvent : true,
6323     forumId: 4
6324 });</code></pre>
6325      * <p>
6326      * <b>Attaching multiple handlers in 1 call</b><br>
6327       * The method also allows for a single argument to be passed which is a config object containing properties
6328      * which specify multiple handlers.
6329      * <p>
6330      * Code:<pre><code>
6331 el.on({
6332     'click' : {
6333         fn: this.onClick
6334         scope: this,
6335         delay: 100
6336     },
6337     'mouseover' : {
6338         fn: this.onMouseOver
6339         scope: this
6340     },
6341     'mouseout' : {
6342         fn: this.onMouseOut
6343         scope: this
6344     }
6345 });</code></pre>
6346      * <p>
6347      * Or a shorthand syntax:<br>
6348      * Code:<pre><code>
6349 el.on({
6350     'click' : this.onClick,
6351     'mouseover' : this.onMouseOver,
6352     'mouseout' : this.onMouseOut
6353     scope: this
6354 });</code></pre>
6355      */
6356         addListener : function(element, eventName, fn, scope, options){
6357             if(typeof eventName == "object"){
6358                 var o = eventName;
6359                 for(var e in o){
6360                     if(propRe.test(e)){
6361                         continue;
6362                     }
6363                     if(typeof o[e] == "function"){
6364                         // shared options
6365                         listen(element, e, o, o[e], o.scope);
6366                     }else{
6367                         // individual options
6368                         listen(element, e, o[e]);
6369                     }
6370                 }
6371                 return;
6372             }
6373             return listen(element, eventName, options, fn, scope);
6374         },
6375         
6376         /**
6377          * Removes an event handler
6378          *
6379          * @param {String/HTMLElement}   element        The id or html element to remove the 
6380          *                             event from
6381          * @param {String}   eventName     The type of event
6382          * @param {Function} fn
6383          * @return {Boolean} True if a listener was actually removed
6384          */
6385         removeListener : function(element, eventName, fn){
6386             return stopListening(element, eventName, fn);
6387         },
6388         
6389         /**
6390          * Fires when the document is ready (before onload and before images are loaded). Can be 
6391          * accessed shorthanded Roo.onReady().
6392          * @param {Function} fn        The method the event invokes
6393          * @param {Object}   scope    An  object that becomes the scope of the handler
6394          * @param {boolean}  options
6395          */
6396         onDocumentReady : function(fn, scope, options){
6397             if(docReadyState){ // if it already fired
6398                 docReadyEvent.addListener(fn, scope, options);
6399                 docReadyEvent.fire();
6400                 docReadyEvent.clearListeners();
6401                 return;
6402             }
6403             if(!docReadyEvent){
6404                 initDocReady();
6405             }
6406             docReadyEvent.addListener(fn, scope, options);
6407         },
6408         
6409         /**
6410          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6411          * @param {Function} fn        The method the event invokes
6412          * @param {Object}   scope    An object that becomes the scope of the handler
6413          * @param {boolean}  options
6414          */
6415         onWindowResize : function(fn, scope, options){
6416             if(!resizeEvent){
6417                 resizeEvent = new Roo.util.Event();
6418                 resizeTask = new Roo.util.DelayedTask(function(){
6419                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6420                 });
6421                 E.on(window, "resize", function(){
6422                     if(Roo.isIE){
6423                         resizeTask.delay(50);
6424                     }else{
6425                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6426                     }
6427                 });
6428             }
6429             resizeEvent.addListener(fn, scope, options);
6430         },
6431
6432         /**
6433          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6434          * @param {Function} fn        The method the event invokes
6435          * @param {Object}   scope    An object that becomes the scope of the handler
6436          * @param {boolean}  options
6437          */
6438         onTextResize : function(fn, scope, options){
6439             if(!textEvent){
6440                 textEvent = new Roo.util.Event();
6441                 var textEl = new Roo.Element(document.createElement('div'));
6442                 textEl.dom.className = 'x-text-resize';
6443                 textEl.dom.innerHTML = 'X';
6444                 textEl.appendTo(document.body);
6445                 textSize = textEl.dom.offsetHeight;
6446                 setInterval(function(){
6447                     if(textEl.dom.offsetHeight != textSize){
6448                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6449                     }
6450                 }, this.textResizeInterval);
6451             }
6452             textEvent.addListener(fn, scope, options);
6453         },
6454
6455         /**
6456          * Removes the passed window resize listener.
6457          * @param {Function} fn        The method the event invokes
6458          * @param {Object}   scope    The scope of handler
6459          */
6460         removeResizeListener : function(fn, scope){
6461             if(resizeEvent){
6462                 resizeEvent.removeListener(fn, scope);
6463             }
6464         },
6465
6466         // private
6467         fireResize : function(){
6468             if(resizeEvent){
6469                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6470             }   
6471         },
6472         /**
6473          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6474          */
6475         ieDeferSrc : false,
6476         /**
6477          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6478          */
6479         textResizeInterval : 50
6480     };
6481     
6482     /**
6483      * Fix for doc tools
6484      * @scopeAlias pub=Roo.EventManager
6485      */
6486     
6487      /**
6488      * Appends an event handler to an element (shorthand for addListener)
6489      * @param {String/HTMLElement}   element        The html element or id to assign the
6490      * @param {String}   eventName The type of event to listen for
6491      * @param {Function} handler The method the event invokes
6492      * @param {Object}   scope (optional) The scope in which to execute the handler
6493      * function. The handler function's "this" context.
6494      * @param {Object}   options (optional) An object containing handler configuration
6495      * properties. This may contain any of the following properties:<ul>
6496      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6497      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6498      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6499      * <li>preventDefault {Boolean} True to prevent the default action</li>
6500      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6501      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6502      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6503      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6504      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6505      * by the specified number of milliseconds. If the event fires again within that time, the original
6506      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6507      * </ul><br>
6508      * <p>
6509      * <b>Combining Options</b><br>
6510      * Using the options argument, it is possible to combine different types of listeners:<br>
6511      * <br>
6512      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6513      * Code:<pre><code>
6514 el.on('click', this.onClick, this, {
6515     single: true,
6516     delay: 100,
6517     stopEvent : true,
6518     forumId: 4
6519 });</code></pre>
6520      * <p>
6521      * <b>Attaching multiple handlers in 1 call</b><br>
6522       * The method also allows for a single argument to be passed which is a config object containing properties
6523      * which specify multiple handlers.
6524      * <p>
6525      * Code:<pre><code>
6526 el.on({
6527     'click' : {
6528         fn: this.onClick
6529         scope: this,
6530         delay: 100
6531     },
6532     'mouseover' : {
6533         fn: this.onMouseOver
6534         scope: this
6535     },
6536     'mouseout' : {
6537         fn: this.onMouseOut
6538         scope: this
6539     }
6540 });</code></pre>
6541      * <p>
6542      * Or a shorthand syntax:<br>
6543      * Code:<pre><code>
6544 el.on({
6545     'click' : this.onClick,
6546     'mouseover' : this.onMouseOver,
6547     'mouseout' : this.onMouseOut
6548     scope: this
6549 });</code></pre>
6550      */
6551     pub.on = pub.addListener;
6552     pub.un = pub.removeListener;
6553
6554     pub.stoppedMouseDownEvent = new Roo.util.Event();
6555     return pub;
6556 }();
6557 /**
6558   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6559   * @param {Function} fn        The method the event invokes
6560   * @param {Object}   scope    An  object that becomes the scope of the handler
6561   * @param {boolean}  override If true, the obj passed in becomes
6562   *                             the execution scope of the listener
6563   * @member Roo
6564   * @method onReady
6565  */
6566 Roo.onReady = Roo.EventManager.onDocumentReady;
6567
6568 Roo.onReady(function(){
6569     var bd = Roo.get(document.body);
6570     if(!bd){ return; }
6571
6572     var cls = [
6573             Roo.isIE ? "roo-ie"
6574             : Roo.isGecko ? "roo-gecko"
6575             : Roo.isOpera ? "roo-opera"
6576             : Roo.isSafari ? "roo-safari" : ""];
6577
6578     if(Roo.isMac){
6579         cls.push("roo-mac");
6580     }
6581     if(Roo.isLinux){
6582         cls.push("roo-linux");
6583     }
6584     if(Roo.isIOS){
6585         cls.push("roo-ios");
6586     }
6587     if(Roo.isTouch){
6588         cls.push("roo-touch");
6589     }
6590     if(Roo.isBorderBox){
6591         cls.push('roo-border-box');
6592     }
6593     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6594         var p = bd.dom.parentNode;
6595         if(p){
6596             p.className += ' roo-strict';
6597         }
6598     }
6599     bd.addClass(cls.join(' '));
6600 });
6601
6602 /**
6603  * @class Roo.EventObject
6604  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6605  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6606  * Example:
6607  * <pre><code>
6608  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6609     e.preventDefault();
6610     var target = e.getTarget();
6611     ...
6612  }
6613  var myDiv = Roo.get("myDiv");
6614  myDiv.on("click", handleClick);
6615  //or
6616  Roo.EventManager.on("myDiv", 'click', handleClick);
6617  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6618  </code></pre>
6619  * @singleton
6620  */
6621 Roo.EventObject = function(){
6622     
6623     var E = Roo.lib.Event;
6624     
6625     // safari keypress events for special keys return bad keycodes
6626     var safariKeys = {
6627         63234 : 37, // left
6628         63235 : 39, // right
6629         63232 : 38, // up
6630         63233 : 40, // down
6631         63276 : 33, // page up
6632         63277 : 34, // page down
6633         63272 : 46, // delete
6634         63273 : 36, // home
6635         63275 : 35  // end
6636     };
6637
6638     // normalize button clicks
6639     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6640                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6641
6642     Roo.EventObjectImpl = function(e){
6643         if(e){
6644             this.setEvent(e.browserEvent || e);
6645         }
6646     };
6647     Roo.EventObjectImpl.prototype = {
6648         /**
6649          * Used to fix doc tools.
6650          * @scope Roo.EventObject.prototype
6651          */
6652             
6653
6654         
6655         
6656         /** The normal browser event */
6657         browserEvent : null,
6658         /** The button pressed in a mouse event */
6659         button : -1,
6660         /** True if the shift key was down during the event */
6661         shiftKey : false,
6662         /** True if the control key was down during the event */
6663         ctrlKey : false,
6664         /** True if the alt key was down during the event */
6665         altKey : false,
6666
6667         /** Key constant 
6668         * @type Number */
6669         BACKSPACE : 8,
6670         /** Key constant 
6671         * @type Number */
6672         TAB : 9,
6673         /** Key constant 
6674         * @type Number */
6675         RETURN : 13,
6676         /** Key constant 
6677         * @type Number */
6678         ENTER : 13,
6679         /** Key constant 
6680         * @type Number */
6681         SHIFT : 16,
6682         /** Key constant 
6683         * @type Number */
6684         CONTROL : 17,
6685         /** Key constant 
6686         * @type Number */
6687         ESC : 27,
6688         /** Key constant 
6689         * @type Number */
6690         SPACE : 32,
6691         /** Key constant 
6692         * @type Number */
6693         PAGEUP : 33,
6694         /** Key constant 
6695         * @type Number */
6696         PAGEDOWN : 34,
6697         /** Key constant 
6698         * @type Number */
6699         END : 35,
6700         /** Key constant 
6701         * @type Number */
6702         HOME : 36,
6703         /** Key constant 
6704         * @type Number */
6705         LEFT : 37,
6706         /** Key constant 
6707         * @type Number */
6708         UP : 38,
6709         /** Key constant 
6710         * @type Number */
6711         RIGHT : 39,
6712         /** Key constant 
6713         * @type Number */
6714         DOWN : 40,
6715         /** Key constant 
6716         * @type Number */
6717         DELETE : 46,
6718         /** Key constant 
6719         * @type Number */
6720         F5 : 116,
6721
6722            /** @private */
6723         setEvent : function(e){
6724             if(e == this || (e && e.browserEvent)){ // already wrapped
6725                 return e;
6726             }
6727             this.browserEvent = e;
6728             if(e){
6729                 // normalize buttons
6730                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6731                 if(e.type == 'click' && this.button == -1){
6732                     this.button = 0;
6733                 }
6734                 this.type = e.type;
6735                 this.shiftKey = e.shiftKey;
6736                 // mac metaKey behaves like ctrlKey
6737                 this.ctrlKey = e.ctrlKey || e.metaKey;
6738                 this.altKey = e.altKey;
6739                 // in getKey these will be normalized for the mac
6740                 this.keyCode = e.keyCode;
6741                 // keyup warnings on firefox.
6742                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6743                 // cache the target for the delayed and or buffered events
6744                 this.target = E.getTarget(e);
6745                 // same for XY
6746                 this.xy = E.getXY(e);
6747             }else{
6748                 this.button = -1;
6749                 this.shiftKey = false;
6750                 this.ctrlKey = false;
6751                 this.altKey = false;
6752                 this.keyCode = 0;
6753                 this.charCode =0;
6754                 this.target = null;
6755                 this.xy = [0, 0];
6756             }
6757             return this;
6758         },
6759
6760         /**
6761          * Stop the event (preventDefault and stopPropagation)
6762          */
6763         stopEvent : function(){
6764             if(this.browserEvent){
6765                 if(this.browserEvent.type == 'mousedown'){
6766                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6767                 }
6768                 E.stopEvent(this.browserEvent);
6769             }
6770         },
6771
6772         /**
6773          * Prevents the browsers default handling of the event.
6774          */
6775         preventDefault : function(){
6776             if(this.browserEvent){
6777                 E.preventDefault(this.browserEvent);
6778             }
6779         },
6780
6781         /** @private */
6782         isNavKeyPress : function(){
6783             var k = this.keyCode;
6784             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6785             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6786         },
6787
6788         isSpecialKey : function(){
6789             var k = this.keyCode;
6790             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6791             (k == 16) || (k == 17) ||
6792             (k >= 18 && k <= 20) ||
6793             (k >= 33 && k <= 35) ||
6794             (k >= 36 && k <= 39) ||
6795             (k >= 44 && k <= 45);
6796         },
6797         /**
6798          * Cancels bubbling of the event.
6799          */
6800         stopPropagation : function(){
6801             if(this.browserEvent){
6802                 if(this.type == 'mousedown'){
6803                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6804                 }
6805                 E.stopPropagation(this.browserEvent);
6806             }
6807         },
6808
6809         /**
6810          * Gets the key code for the event.
6811          * @return {Number}
6812          */
6813         getCharCode : function(){
6814             return this.charCode || this.keyCode;
6815         },
6816
6817         /**
6818          * Returns a normalized keyCode for the event.
6819          * @return {Number} The key code
6820          */
6821         getKey : function(){
6822             var k = this.keyCode || this.charCode;
6823             return Roo.isSafari ? (safariKeys[k] || k) : k;
6824         },
6825
6826         /**
6827          * Gets the x coordinate of the event.
6828          * @return {Number}
6829          */
6830         getPageX : function(){
6831             return this.xy[0];
6832         },
6833
6834         /**
6835          * Gets the y coordinate of the event.
6836          * @return {Number}
6837          */
6838         getPageY : function(){
6839             return this.xy[1];
6840         },
6841
6842         /**
6843          * Gets the time of the event.
6844          * @return {Number}
6845          */
6846         getTime : function(){
6847             if(this.browserEvent){
6848                 return E.getTime(this.browserEvent);
6849             }
6850             return null;
6851         },
6852
6853         /**
6854          * Gets the page coordinates of the event.
6855          * @return {Array} The xy values like [x, y]
6856          */
6857         getXY : function(){
6858             return this.xy;
6859         },
6860
6861         /**
6862          * Gets the target for the event.
6863          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6864          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6865                 search as a number or element (defaults to 10 || document.body)
6866          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6867          * @return {HTMLelement}
6868          */
6869         getTarget : function(selector, maxDepth, returnEl){
6870             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6871         },
6872         /**
6873          * Gets the related target.
6874          * @return {HTMLElement}
6875          */
6876         getRelatedTarget : function(){
6877             if(this.browserEvent){
6878                 return E.getRelatedTarget(this.browserEvent);
6879             }
6880             return null;
6881         },
6882
6883         /**
6884          * Normalizes mouse wheel delta across browsers
6885          * @return {Number} The delta
6886          */
6887         getWheelDelta : function(){
6888             var e = this.browserEvent;
6889             var delta = 0;
6890             if(e.wheelDelta){ /* IE/Opera. */
6891                 delta = e.wheelDelta/120;
6892             }else if(e.detail){ /* Mozilla case. */
6893                 delta = -e.detail/3;
6894             }
6895             return delta;
6896         },
6897
6898         /**
6899          * Returns true if the control, meta, shift or alt key was pressed during this event.
6900          * @return {Boolean}
6901          */
6902         hasModifier : function(){
6903             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6904         },
6905
6906         /**
6907          * Returns true if the target of this event equals el or is a child of el
6908          * @param {String/HTMLElement/Element} el
6909          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6910          * @return {Boolean}
6911          */
6912         within : function(el, related){
6913             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6914             return t && Roo.fly(el).contains(t);
6915         },
6916
6917         getPoint : function(){
6918             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6919         }
6920     };
6921
6922     return new Roo.EventObjectImpl();
6923 }();
6924             
6925     /*
6926  * Based on:
6927  * Ext JS Library 1.1.1
6928  * Copyright(c) 2006-2007, Ext JS, LLC.
6929  *
6930  * Originally Released Under LGPL - original licence link has changed is not relivant.
6931  *
6932  * Fork - LGPL
6933  * <script type="text/javascript">
6934  */
6935
6936  
6937 // was in Composite Element!??!?!
6938  
6939 (function(){
6940     var D = Roo.lib.Dom;
6941     var E = Roo.lib.Event;
6942     var A = Roo.lib.Anim;
6943
6944     // local style camelizing for speed
6945     var propCache = {};
6946     var camelRe = /(-[a-z])/gi;
6947     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6948     var view = document.defaultView;
6949
6950 /**
6951  * @class Roo.Element
6952  * Represents an Element in the DOM.<br><br>
6953  * Usage:<br>
6954 <pre><code>
6955 var el = Roo.get("my-div");
6956
6957 // or with getEl
6958 var el = getEl("my-div");
6959
6960 // or with a DOM element
6961 var el = Roo.get(myDivElement);
6962 </code></pre>
6963  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6964  * each call instead of constructing a new one.<br><br>
6965  * <b>Animations</b><br />
6966  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6967  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6968 <pre>
6969 Option    Default   Description
6970 --------- --------  ---------------------------------------------
6971 duration  .35       The duration of the animation in seconds
6972 easing    easeOut   The YUI easing method
6973 callback  none      A function to execute when the anim completes
6974 scope     this      The scope (this) of the callback function
6975 </pre>
6976 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6977 * manipulate the animation. Here's an example:
6978 <pre><code>
6979 var el = Roo.get("my-div");
6980
6981 // no animation
6982 el.setWidth(100);
6983
6984 // default animation
6985 el.setWidth(100, true);
6986
6987 // animation with some options set
6988 el.setWidth(100, {
6989     duration: 1,
6990     callback: this.foo,
6991     scope: this
6992 });
6993
6994 // using the "anim" property to get the Anim object
6995 var opt = {
6996     duration: 1,
6997     callback: this.foo,
6998     scope: this
6999 };
7000 el.setWidth(100, opt);
7001 ...
7002 if(opt.anim.isAnimated()){
7003     opt.anim.stop();
7004 }
7005 </code></pre>
7006 * <b> Composite (Collections of) Elements</b><br />
7007  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7008  * @constructor Create a new Element directly.
7009  * @param {String/HTMLElement} element
7010  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7011  */
7012     Roo.Element = function(element, forceNew){
7013         var dom = typeof element == "string" ?
7014                 document.getElementById(element) : element;
7015         if(!dom){ // invalid id/element
7016             return null;
7017         }
7018         var id = dom.id;
7019         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7020             return Roo.Element.cache[id];
7021         }
7022
7023         /**
7024          * The DOM element
7025          * @type HTMLElement
7026          */
7027         this.dom = dom;
7028
7029         /**
7030          * The DOM element ID
7031          * @type String
7032          */
7033         this.id = id || Roo.id(dom);
7034     };
7035
7036     var El = Roo.Element;
7037
7038     El.prototype = {
7039         /**
7040          * The element's default display mode  (defaults to "")
7041          * @type String
7042          */
7043         originalDisplay : "",
7044
7045         visibilityMode : 1,
7046         /**
7047          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7048          * @type String
7049          */
7050         defaultUnit : "px",
7051         
7052         /**
7053          * Sets the element's visibility mode. When setVisible() is called it
7054          * will use this to determine whether to set the visibility or the display property.
7055          * @param visMode Element.VISIBILITY or Element.DISPLAY
7056          * @return {Roo.Element} this
7057          */
7058         setVisibilityMode : function(visMode){
7059             this.visibilityMode = visMode;
7060             return this;
7061         },
7062         /**
7063          * Convenience method for setVisibilityMode(Element.DISPLAY)
7064          * @param {String} display (optional) What to set display to when visible
7065          * @return {Roo.Element} this
7066          */
7067         enableDisplayMode : function(display){
7068             this.setVisibilityMode(El.DISPLAY);
7069             if(typeof display != "undefined") this.originalDisplay = display;
7070             return this;
7071         },
7072
7073         /**
7074          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7075          * @param {String} selector The simple selector to test
7076          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7077                 search as a number or element (defaults to 10 || document.body)
7078          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7079          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7080          */
7081         findParent : function(simpleSelector, maxDepth, returnEl){
7082             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7083             maxDepth = maxDepth || 50;
7084             if(typeof maxDepth != "number"){
7085                 stopEl = Roo.getDom(maxDepth);
7086                 maxDepth = 10;
7087             }
7088             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7089                 if(dq.is(p, simpleSelector)){
7090                     return returnEl ? Roo.get(p) : p;
7091                 }
7092                 depth++;
7093                 p = p.parentNode;
7094             }
7095             return null;
7096         },
7097
7098
7099         /**
7100          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7101          * @param {String} selector The simple selector to test
7102          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7103                 search as a number or element (defaults to 10 || document.body)
7104          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7105          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7106          */
7107         findParentNode : function(simpleSelector, maxDepth, returnEl){
7108             var p = Roo.fly(this.dom.parentNode, '_internal');
7109             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7110         },
7111
7112         /**
7113          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7114          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7115          * @param {String} selector The simple selector to test
7116          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7117                 search as a number or element (defaults to 10 || document.body)
7118          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7119          */
7120         up : function(simpleSelector, maxDepth){
7121             return this.findParentNode(simpleSelector, maxDepth, true);
7122         },
7123
7124
7125
7126         /**
7127          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7128          * @param {String} selector The simple selector to test
7129          * @return {Boolean} True if this element matches the selector, else false
7130          */
7131         is : function(simpleSelector){
7132             return Roo.DomQuery.is(this.dom, simpleSelector);
7133         },
7134
7135         /**
7136          * Perform animation on this element.
7137          * @param {Object} args The YUI animation control args
7138          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7139          * @param {Function} onComplete (optional) Function to call when animation completes
7140          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7141          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7142          * @return {Roo.Element} this
7143          */
7144         animate : function(args, duration, onComplete, easing, animType){
7145             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7146             return this;
7147         },
7148
7149         /*
7150          * @private Internal animation call
7151          */
7152         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7153             animType = animType || 'run';
7154             opt = opt || {};
7155             var anim = Roo.lib.Anim[animType](
7156                 this.dom, args,
7157                 (opt.duration || defaultDur) || .35,
7158                 (opt.easing || defaultEase) || 'easeOut',
7159                 function(){
7160                     Roo.callback(cb, this);
7161                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7162                 },
7163                 this
7164             );
7165             opt.anim = anim;
7166             return anim;
7167         },
7168
7169         // private legacy anim prep
7170         preanim : function(a, i){
7171             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7172         },
7173
7174         /**
7175          * Removes worthless text nodes
7176          * @param {Boolean} forceReclean (optional) By default the element
7177          * keeps track if it has been cleaned already so
7178          * you can call this over and over. However, if you update the element and
7179          * need to force a reclean, you can pass true.
7180          */
7181         clean : function(forceReclean){
7182             if(this.isCleaned && forceReclean !== true){
7183                 return this;
7184             }
7185             var ns = /\S/;
7186             var d = this.dom, n = d.firstChild, ni = -1;
7187             while(n){
7188                 var nx = n.nextSibling;
7189                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7190                     d.removeChild(n);
7191                 }else{
7192                     n.nodeIndex = ++ni;
7193                 }
7194                 n = nx;
7195             }
7196             this.isCleaned = true;
7197             return this;
7198         },
7199
7200         // private
7201         calcOffsetsTo : function(el){
7202             el = Roo.get(el);
7203             var d = el.dom;
7204             var restorePos = false;
7205             if(el.getStyle('position') == 'static'){
7206                 el.position('relative');
7207                 restorePos = true;
7208             }
7209             var x = 0, y =0;
7210             var op = this.dom;
7211             while(op && op != d && op.tagName != 'HTML'){
7212                 x+= op.offsetLeft;
7213                 y+= op.offsetTop;
7214                 op = op.offsetParent;
7215             }
7216             if(restorePos){
7217                 el.position('static');
7218             }
7219             return [x, y];
7220         },
7221
7222         /**
7223          * Scrolls this element into view within the passed container.
7224          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7225          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7226          * @return {Roo.Element} this
7227          */
7228         scrollIntoView : function(container, hscroll){
7229             var c = Roo.getDom(container) || document.body;
7230             var el = this.dom;
7231
7232             var o = this.calcOffsetsTo(c),
7233                 l = o[0],
7234                 t = o[1],
7235                 b = t+el.offsetHeight,
7236                 r = l+el.offsetWidth;
7237
7238             var ch = c.clientHeight;
7239             var ct = parseInt(c.scrollTop, 10);
7240             var cl = parseInt(c.scrollLeft, 10);
7241             var cb = ct + ch;
7242             var cr = cl + c.clientWidth;
7243
7244             if(t < ct){
7245                 c.scrollTop = t;
7246             }else if(b > cb){
7247                 c.scrollTop = b-ch;
7248             }
7249
7250             if(hscroll !== false){
7251                 if(l < cl){
7252                     c.scrollLeft = l;
7253                 }else if(r > cr){
7254                     c.scrollLeft = r-c.clientWidth;
7255                 }
7256             }
7257             return this;
7258         },
7259
7260         // private
7261         scrollChildIntoView : function(child, hscroll){
7262             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7263         },
7264
7265         /**
7266          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7267          * the new height may not be available immediately.
7268          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7269          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7270          * @param {Function} onComplete (optional) Function to call when animation completes
7271          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7272          * @return {Roo.Element} this
7273          */
7274         autoHeight : function(animate, duration, onComplete, easing){
7275             var oldHeight = this.getHeight();
7276             this.clip();
7277             this.setHeight(1); // force clipping
7278             setTimeout(function(){
7279                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7280                 if(!animate){
7281                     this.setHeight(height);
7282                     this.unclip();
7283                     if(typeof onComplete == "function"){
7284                         onComplete();
7285                     }
7286                 }else{
7287                     this.setHeight(oldHeight); // restore original height
7288                     this.setHeight(height, animate, duration, function(){
7289                         this.unclip();
7290                         if(typeof onComplete == "function") onComplete();
7291                     }.createDelegate(this), easing);
7292                 }
7293             }.createDelegate(this), 0);
7294             return this;
7295         },
7296
7297         /**
7298          * Returns true if this element is an ancestor of the passed element
7299          * @param {HTMLElement/String} el The element to check
7300          * @return {Boolean} True if this element is an ancestor of el, else false
7301          */
7302         contains : function(el){
7303             if(!el){return false;}
7304             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7305         },
7306
7307         /**
7308          * Checks whether the element is currently visible using both visibility and display properties.
7309          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7310          * @return {Boolean} True if the element is currently visible, else false
7311          */
7312         isVisible : function(deep) {
7313             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7314             if(deep !== true || !vis){
7315                 return vis;
7316             }
7317             var p = this.dom.parentNode;
7318             while(p && p.tagName.toLowerCase() != "body"){
7319                 if(!Roo.fly(p, '_isVisible').isVisible()){
7320                     return false;
7321                 }
7322                 p = p.parentNode;
7323             }
7324             return true;
7325         },
7326
7327         /**
7328          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7329          * @param {String} selector The CSS selector
7330          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7331          * @return {CompositeElement/CompositeElementLite} The composite element
7332          */
7333         select : function(selector, unique){
7334             return El.select(selector, unique, this.dom);
7335         },
7336
7337         /**
7338          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7339          * @param {String} selector The CSS selector
7340          * @return {Array} An array of the matched nodes
7341          */
7342         query : function(selector, unique){
7343             return Roo.DomQuery.select(selector, this.dom);
7344         },
7345
7346         /**
7347          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7348          * @param {String} selector The CSS selector
7349          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7350          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7351          */
7352         child : function(selector, returnDom){
7353             var n = Roo.DomQuery.selectNode(selector, this.dom);
7354             return returnDom ? n : Roo.get(n);
7355         },
7356
7357         /**
7358          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7359          * @param {String} selector The CSS selector
7360          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7361          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7362          */
7363         down : function(selector, returnDom){
7364             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7365             return returnDom ? n : Roo.get(n);
7366         },
7367
7368         /**
7369          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7370          * @param {String} group The group the DD object is member of
7371          * @param {Object} config The DD config object
7372          * @param {Object} overrides An object containing methods to override/implement on the DD object
7373          * @return {Roo.dd.DD} The DD object
7374          */
7375         initDD : function(group, config, overrides){
7376             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7377             return Roo.apply(dd, overrides);
7378         },
7379
7380         /**
7381          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7382          * @param {String} group The group the DDProxy object is member of
7383          * @param {Object} config The DDProxy config object
7384          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7385          * @return {Roo.dd.DDProxy} The DDProxy object
7386          */
7387         initDDProxy : function(group, config, overrides){
7388             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7389             return Roo.apply(dd, overrides);
7390         },
7391
7392         /**
7393          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7394          * @param {String} group The group the DDTarget object is member of
7395          * @param {Object} config The DDTarget config object
7396          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7397          * @return {Roo.dd.DDTarget} The DDTarget object
7398          */
7399         initDDTarget : function(group, config, overrides){
7400             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7401             return Roo.apply(dd, overrides);
7402         },
7403
7404         /**
7405          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7406          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7407          * @param {Boolean} visible Whether the element is visible
7408          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7409          * @return {Roo.Element} this
7410          */
7411          setVisible : function(visible, animate){
7412             if(!animate || !A){
7413                 if(this.visibilityMode == El.DISPLAY){
7414                     this.setDisplayed(visible);
7415                 }else{
7416                     this.fixDisplay();
7417                     this.dom.style.visibility = visible ? "visible" : "hidden";
7418                 }
7419             }else{
7420                 // closure for composites
7421                 var dom = this.dom;
7422                 var visMode = this.visibilityMode;
7423                 if(visible){
7424                     this.setOpacity(.01);
7425                     this.setVisible(true);
7426                 }
7427                 this.anim({opacity: { to: (visible?1:0) }},
7428                       this.preanim(arguments, 1),
7429                       null, .35, 'easeIn', function(){
7430                          if(!visible){
7431                              if(visMode == El.DISPLAY){
7432                                  dom.style.display = "none";
7433                              }else{
7434                                  dom.style.visibility = "hidden";
7435                              }
7436                              Roo.get(dom).setOpacity(1);
7437                          }
7438                      });
7439             }
7440             return this;
7441         },
7442
7443         /**
7444          * Returns true if display is not "none"
7445          * @return {Boolean}
7446          */
7447         isDisplayed : function() {
7448             return this.getStyle("display") != "none";
7449         },
7450
7451         /**
7452          * Toggles the element's visibility or display, depending on visibility mode.
7453          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7454          * @return {Roo.Element} this
7455          */
7456         toggle : function(animate){
7457             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7458             return this;
7459         },
7460
7461         /**
7462          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7463          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7464          * @return {Roo.Element} this
7465          */
7466         setDisplayed : function(value) {
7467             if(typeof value == "boolean"){
7468                value = value ? this.originalDisplay : "none";
7469             }
7470             this.setStyle("display", value);
7471             return this;
7472         },
7473
7474         /**
7475          * Tries to focus the element. Any exceptions are caught and ignored.
7476          * @return {Roo.Element} this
7477          */
7478         focus : function() {
7479             try{
7480                 this.dom.focus();
7481             }catch(e){}
7482             return this;
7483         },
7484
7485         /**
7486          * Tries to blur the element. Any exceptions are caught and ignored.
7487          * @return {Roo.Element} this
7488          */
7489         blur : function() {
7490             try{
7491                 this.dom.blur();
7492             }catch(e){}
7493             return this;
7494         },
7495
7496         /**
7497          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7498          * @param {String/Array} className The CSS class to add, or an array of classes
7499          * @return {Roo.Element} this
7500          */
7501         addClass : function(className){
7502             if(className instanceof Array){
7503                 for(var i = 0, len = className.length; i < len; i++) {
7504                     this.addClass(className[i]);
7505                 }
7506             }else{
7507                 if(className && !this.hasClass(className)){
7508                     this.dom.className = this.dom.className + " " + className;
7509                 }
7510             }
7511             return this;
7512         },
7513
7514         /**
7515          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7516          * @param {String/Array} className The CSS class to add, or an array of classes
7517          * @return {Roo.Element} this
7518          */
7519         radioClass : function(className){
7520             var siblings = this.dom.parentNode.childNodes;
7521             for(var i = 0; i < siblings.length; i++) {
7522                 var s = siblings[i];
7523                 if(s.nodeType == 1){
7524                     Roo.get(s).removeClass(className);
7525                 }
7526             }
7527             this.addClass(className);
7528             return this;
7529         },
7530
7531         /**
7532          * Removes one or more CSS classes from the element.
7533          * @param {String/Array} className The CSS class to remove, or an array of classes
7534          * @return {Roo.Element} this
7535          */
7536         removeClass : function(className){
7537             if(!className || !this.dom.className){
7538                 return this;
7539             }
7540             if(className instanceof Array){
7541                 for(var i = 0, len = className.length; i < len; i++) {
7542                     this.removeClass(className[i]);
7543                 }
7544             }else{
7545                 if(this.hasClass(className)){
7546                     var re = this.classReCache[className];
7547                     if (!re) {
7548                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7549                        this.classReCache[className] = re;
7550                     }
7551                     this.dom.className =
7552                         this.dom.className.replace(re, " ");
7553                 }
7554             }
7555             return this;
7556         },
7557
7558         // private
7559         classReCache: {},
7560
7561         /**
7562          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7563          * @param {String} className The CSS class to toggle
7564          * @return {Roo.Element} this
7565          */
7566         toggleClass : function(className){
7567             if(this.hasClass(className)){
7568                 this.removeClass(className);
7569             }else{
7570                 this.addClass(className);
7571             }
7572             return this;
7573         },
7574
7575         /**
7576          * Checks if the specified CSS class exists on this element's DOM node.
7577          * @param {String} className The CSS class to check for
7578          * @return {Boolean} True if the class exists, else false
7579          */
7580         hasClass : function(className){
7581             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7582         },
7583
7584         /**
7585          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7586          * @param {String} oldClassName The CSS class to replace
7587          * @param {String} newClassName The replacement CSS class
7588          * @return {Roo.Element} this
7589          */
7590         replaceClass : function(oldClassName, newClassName){
7591             this.removeClass(oldClassName);
7592             this.addClass(newClassName);
7593             return this;
7594         },
7595
7596         /**
7597          * Returns an object with properties matching the styles requested.
7598          * For example, el.getStyles('color', 'font-size', 'width') might return
7599          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7600          * @param {String} style1 A style name
7601          * @param {String} style2 A style name
7602          * @param {String} etc.
7603          * @return {Object} The style object
7604          */
7605         getStyles : function(){
7606             var a = arguments, len = a.length, r = {};
7607             for(var i = 0; i < len; i++){
7608                 r[a[i]] = this.getStyle(a[i]);
7609             }
7610             return r;
7611         },
7612
7613         /**
7614          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7615          * @param {String} property The style property whose value is returned.
7616          * @return {String} The current value of the style property for this element.
7617          */
7618         getStyle : function(){
7619             return view && view.getComputedStyle ?
7620                 function(prop){
7621                     var el = this.dom, v, cs, camel;
7622                     if(prop == 'float'){
7623                         prop = "cssFloat";
7624                     }
7625                     if(el.style && (v = el.style[prop])){
7626                         return v;
7627                     }
7628                     if(cs = view.getComputedStyle(el, "")){
7629                         if(!(camel = propCache[prop])){
7630                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7631                         }
7632                         return cs[camel];
7633                     }
7634                     return null;
7635                 } :
7636                 function(prop){
7637                     var el = this.dom, v, cs, camel;
7638                     if(prop == 'opacity'){
7639                         if(typeof el.style.filter == 'string'){
7640                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7641                             if(m){
7642                                 var fv = parseFloat(m[1]);
7643                                 if(!isNaN(fv)){
7644                                     return fv ? fv / 100 : 0;
7645                                 }
7646                             }
7647                         }
7648                         return 1;
7649                     }else if(prop == 'float'){
7650                         prop = "styleFloat";
7651                     }
7652                     if(!(camel = propCache[prop])){
7653                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7654                     }
7655                     if(v = el.style[camel]){
7656                         return v;
7657                     }
7658                     if(cs = el.currentStyle){
7659                         return cs[camel];
7660                     }
7661                     return null;
7662                 };
7663         }(),
7664
7665         /**
7666          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7667          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7668          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7669          * @return {Roo.Element} this
7670          */
7671         setStyle : function(prop, value){
7672             if(typeof prop == "string"){
7673                 
7674                 if (prop == 'float') {
7675                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7676                     return this;
7677                 }
7678                 
7679                 var camel;
7680                 if(!(camel = propCache[prop])){
7681                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7682                 }
7683                 
7684                 if(camel == 'opacity') {
7685                     this.setOpacity(value);
7686                 }else{
7687                     this.dom.style[camel] = value;
7688                 }
7689             }else{
7690                 for(var style in prop){
7691                     if(typeof prop[style] != "function"){
7692                        this.setStyle(style, prop[style]);
7693                     }
7694                 }
7695             }
7696             return this;
7697         },
7698
7699         /**
7700          * More flexible version of {@link #setStyle} for setting style properties.
7701          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7702          * a function which returns such a specification.
7703          * @return {Roo.Element} this
7704          */
7705         applyStyles : function(style){
7706             Roo.DomHelper.applyStyles(this.dom, style);
7707             return this;
7708         },
7709
7710         /**
7711           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7712           * @return {Number} The X position of the element
7713           */
7714         getX : function(){
7715             return D.getX(this.dom);
7716         },
7717
7718         /**
7719           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7720           * @return {Number} The Y position of the element
7721           */
7722         getY : function(){
7723             return D.getY(this.dom);
7724         },
7725
7726         /**
7727           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7728           * @return {Array} The XY position of the element
7729           */
7730         getXY : function(){
7731             return D.getXY(this.dom);
7732         },
7733
7734         /**
7735          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7736          * @param {Number} The X position of the element
7737          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7738          * @return {Roo.Element} this
7739          */
7740         setX : function(x, animate){
7741             if(!animate || !A){
7742                 D.setX(this.dom, x);
7743             }else{
7744                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7745             }
7746             return this;
7747         },
7748
7749         /**
7750          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7751          * @param {Number} The Y position of the element
7752          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7753          * @return {Roo.Element} this
7754          */
7755         setY : function(y, animate){
7756             if(!animate || !A){
7757                 D.setY(this.dom, y);
7758             }else{
7759                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7760             }
7761             return this;
7762         },
7763
7764         /**
7765          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7766          * @param {String} left The left CSS property value
7767          * @return {Roo.Element} this
7768          */
7769         setLeft : function(left){
7770             this.setStyle("left", this.addUnits(left));
7771             return this;
7772         },
7773
7774         /**
7775          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7776          * @param {String} top The top CSS property value
7777          * @return {Roo.Element} this
7778          */
7779         setTop : function(top){
7780             this.setStyle("top", this.addUnits(top));
7781             return this;
7782         },
7783
7784         /**
7785          * Sets the element's CSS right style.
7786          * @param {String} right The right CSS property value
7787          * @return {Roo.Element} this
7788          */
7789         setRight : function(right){
7790             this.setStyle("right", this.addUnits(right));
7791             return this;
7792         },
7793
7794         /**
7795          * Sets the element's CSS bottom style.
7796          * @param {String} bottom The bottom CSS property value
7797          * @return {Roo.Element} this
7798          */
7799         setBottom : function(bottom){
7800             this.setStyle("bottom", this.addUnits(bottom));
7801             return this;
7802         },
7803
7804         /**
7805          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7806          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7807          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7808          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7809          * @return {Roo.Element} this
7810          */
7811         setXY : function(pos, animate){
7812             if(!animate || !A){
7813                 D.setXY(this.dom, pos);
7814             }else{
7815                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7816             }
7817             return this;
7818         },
7819
7820         /**
7821          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7822          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7823          * @param {Number} x X value for new position (coordinates are page-based)
7824          * @param {Number} y Y value for new position (coordinates are page-based)
7825          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7826          * @return {Roo.Element} this
7827          */
7828         setLocation : function(x, y, animate){
7829             this.setXY([x, y], this.preanim(arguments, 2));
7830             return this;
7831         },
7832
7833         /**
7834          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7835          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7836          * @param {Number} x X value for new position (coordinates are page-based)
7837          * @param {Number} y Y value for new position (coordinates are page-based)
7838          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7839          * @return {Roo.Element} this
7840          */
7841         moveTo : function(x, y, animate){
7842             this.setXY([x, y], this.preanim(arguments, 2));
7843             return this;
7844         },
7845
7846         /**
7847          * Returns the region of the given element.
7848          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7849          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7850          */
7851         getRegion : function(){
7852             return D.getRegion(this.dom);
7853         },
7854
7855         /**
7856          * Returns the offset height of the element
7857          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7858          * @return {Number} The element's height
7859          */
7860         getHeight : function(contentHeight){
7861             var h = this.dom.offsetHeight || 0;
7862             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7863         },
7864
7865         /**
7866          * Returns the offset width of the element
7867          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7868          * @return {Number} The element's width
7869          */
7870         getWidth : function(contentWidth){
7871             var w = this.dom.offsetWidth || 0;
7872             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7873         },
7874
7875         /**
7876          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7877          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7878          * if a height has not been set using CSS.
7879          * @return {Number}
7880          */
7881         getComputedHeight : function(){
7882             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7883             if(!h){
7884                 h = parseInt(this.getStyle('height'), 10) || 0;
7885                 if(!this.isBorderBox()){
7886                     h += this.getFrameWidth('tb');
7887                 }
7888             }
7889             return h;
7890         },
7891
7892         /**
7893          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7894          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7895          * if a width has not been set using CSS.
7896          * @return {Number}
7897          */
7898         getComputedWidth : function(){
7899             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7900             if(!w){
7901                 w = parseInt(this.getStyle('width'), 10) || 0;
7902                 if(!this.isBorderBox()){
7903                     w += this.getFrameWidth('lr');
7904                 }
7905             }
7906             return w;
7907         },
7908
7909         /**
7910          * Returns the size of the element.
7911          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7912          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7913          */
7914         getSize : function(contentSize){
7915             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7916         },
7917
7918         /**
7919          * Returns the width and height of the viewport.
7920          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7921          */
7922         getViewSize : function(){
7923             var d = this.dom, doc = document, aw = 0, ah = 0;
7924             if(d == doc || d == doc.body){
7925                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7926             }else{
7927                 return {
7928                     width : d.clientWidth,
7929                     height: d.clientHeight
7930                 };
7931             }
7932         },
7933
7934         /**
7935          * Returns the value of the "value" attribute
7936          * @param {Boolean} asNumber true to parse the value as a number
7937          * @return {String/Number}
7938          */
7939         getValue : function(asNumber){
7940             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7941         },
7942
7943         // private
7944         adjustWidth : function(width){
7945             if(typeof width == "number"){
7946                 if(this.autoBoxAdjust && !this.isBorderBox()){
7947                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7948                 }
7949                 if(width < 0){
7950                     width = 0;
7951                 }
7952             }
7953             return width;
7954         },
7955
7956         // private
7957         adjustHeight : function(height){
7958             if(typeof height == "number"){
7959                if(this.autoBoxAdjust && !this.isBorderBox()){
7960                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7961                }
7962                if(height < 0){
7963                    height = 0;
7964                }
7965             }
7966             return height;
7967         },
7968
7969         /**
7970          * Set the width of the element
7971          * @param {Number} width The new width
7972          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7973          * @return {Roo.Element} this
7974          */
7975         setWidth : function(width, animate){
7976             width = this.adjustWidth(width);
7977             if(!animate || !A){
7978                 this.dom.style.width = this.addUnits(width);
7979             }else{
7980                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7981             }
7982             return this;
7983         },
7984
7985         /**
7986          * Set the height of the element
7987          * @param {Number} height The new height
7988          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7989          * @return {Roo.Element} this
7990          */
7991          setHeight : function(height, animate){
7992             height = this.adjustHeight(height);
7993             if(!animate || !A){
7994                 this.dom.style.height = this.addUnits(height);
7995             }else{
7996                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7997             }
7998             return this;
7999         },
8000
8001         /**
8002          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8003          * @param {Number} width The new width
8004          * @param {Number} height The new height
8005          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8006          * @return {Roo.Element} this
8007          */
8008          setSize : function(width, height, animate){
8009             if(typeof width == "object"){ // in case of object from getSize()
8010                 height = width.height; width = width.width;
8011             }
8012             width = this.adjustWidth(width); height = this.adjustHeight(height);
8013             if(!animate || !A){
8014                 this.dom.style.width = this.addUnits(width);
8015                 this.dom.style.height = this.addUnits(height);
8016             }else{
8017                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8018             }
8019             return this;
8020         },
8021
8022         /**
8023          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8024          * @param {Number} x X value for new position (coordinates are page-based)
8025          * @param {Number} y Y value for new position (coordinates are page-based)
8026          * @param {Number} width The new width
8027          * @param {Number} height The new height
8028          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8029          * @return {Roo.Element} this
8030          */
8031         setBounds : function(x, y, width, height, animate){
8032             if(!animate || !A){
8033                 this.setSize(width, height);
8034                 this.setLocation(x, y);
8035             }else{
8036                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8037                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8038                               this.preanim(arguments, 4), 'motion');
8039             }
8040             return this;
8041         },
8042
8043         /**
8044          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
8045          * @param {Roo.lib.Region} region The region to fill
8046          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8047          * @return {Roo.Element} this
8048          */
8049         setRegion : function(region, animate){
8050             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8051             return this;
8052         },
8053
8054         /**
8055          * Appends an event handler
8056          *
8057          * @param {String}   eventName     The type of event to append
8058          * @param {Function} fn        The method the event invokes
8059          * @param {Object} scope       (optional) The scope (this object) of the fn
8060          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8061          */
8062         addListener : function(eventName, fn, scope, options){
8063             if (this.dom) {
8064                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8065             }
8066         },
8067
8068         /**
8069          * Removes an event handler from this element
8070          * @param {String} eventName the type of event to remove
8071          * @param {Function} fn the method the event invokes
8072          * @return {Roo.Element} this
8073          */
8074         removeListener : function(eventName, fn){
8075             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8076             return this;
8077         },
8078
8079         /**
8080          * Removes all previous added listeners from this element
8081          * @return {Roo.Element} this
8082          */
8083         removeAllListeners : function(){
8084             E.purgeElement(this.dom);
8085             return this;
8086         },
8087
8088         relayEvent : function(eventName, observable){
8089             this.on(eventName, function(e){
8090                 observable.fireEvent(eventName, e);
8091             });
8092         },
8093
8094         /**
8095          * Set the opacity of the element
8096          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8097          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8098          * @return {Roo.Element} this
8099          */
8100          setOpacity : function(opacity, animate){
8101             if(!animate || !A){
8102                 var s = this.dom.style;
8103                 if(Roo.isIE){
8104                     s.zoom = 1;
8105                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8106                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8107                 }else{
8108                     s.opacity = opacity;
8109                 }
8110             }else{
8111                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8112             }
8113             return this;
8114         },
8115
8116         /**
8117          * Gets the left X coordinate
8118          * @param {Boolean} local True to get the local css position instead of page coordinate
8119          * @return {Number}
8120          */
8121         getLeft : function(local){
8122             if(!local){
8123                 return this.getX();
8124             }else{
8125                 return parseInt(this.getStyle("left"), 10) || 0;
8126             }
8127         },
8128
8129         /**
8130          * Gets the right X coordinate of the element (element X position + element width)
8131          * @param {Boolean} local True to get the local css position instead of page coordinate
8132          * @return {Number}
8133          */
8134         getRight : function(local){
8135             if(!local){
8136                 return this.getX() + this.getWidth();
8137             }else{
8138                 return (this.getLeft(true) + this.getWidth()) || 0;
8139             }
8140         },
8141
8142         /**
8143          * Gets the top Y coordinate
8144          * @param {Boolean} local True to get the local css position instead of page coordinate
8145          * @return {Number}
8146          */
8147         getTop : function(local) {
8148             if(!local){
8149                 return this.getY();
8150             }else{
8151                 return parseInt(this.getStyle("top"), 10) || 0;
8152             }
8153         },
8154
8155         /**
8156          * Gets the bottom Y coordinate of the element (element Y position + element height)
8157          * @param {Boolean} local True to get the local css position instead of page coordinate
8158          * @return {Number}
8159          */
8160         getBottom : function(local){
8161             if(!local){
8162                 return this.getY() + this.getHeight();
8163             }else{
8164                 return (this.getTop(true) + this.getHeight()) || 0;
8165             }
8166         },
8167
8168         /**
8169         * Initializes positioning on this element. If a desired position is not passed, it will make the
8170         * the element positioned relative IF it is not already positioned.
8171         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8172         * @param {Number} zIndex (optional) The zIndex to apply
8173         * @param {Number} x (optional) Set the page X position
8174         * @param {Number} y (optional) Set the page Y position
8175         */
8176         position : function(pos, zIndex, x, y){
8177             if(!pos){
8178                if(this.getStyle('position') == 'static'){
8179                    this.setStyle('position', 'relative');
8180                }
8181             }else{
8182                 this.setStyle("position", pos);
8183             }
8184             if(zIndex){
8185                 this.setStyle("z-index", zIndex);
8186             }
8187             if(x !== undefined && y !== undefined){
8188                 this.setXY([x, y]);
8189             }else if(x !== undefined){
8190                 this.setX(x);
8191             }else if(y !== undefined){
8192                 this.setY(y);
8193             }
8194         },
8195
8196         /**
8197         * Clear positioning back to the default when the document was loaded
8198         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8199         * @return {Roo.Element} this
8200          */
8201         clearPositioning : function(value){
8202             value = value ||'';
8203             this.setStyle({
8204                 "left": value,
8205                 "right": value,
8206                 "top": value,
8207                 "bottom": value,
8208                 "z-index": "",
8209                 "position" : "static"
8210             });
8211             return this;
8212         },
8213
8214         /**
8215         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8216         * snapshot before performing an update and then restoring the element.
8217         * @return {Object}
8218         */
8219         getPositioning : function(){
8220             var l = this.getStyle("left");
8221             var t = this.getStyle("top");
8222             return {
8223                 "position" : this.getStyle("position"),
8224                 "left" : l,
8225                 "right" : l ? "" : this.getStyle("right"),
8226                 "top" : t,
8227                 "bottom" : t ? "" : this.getStyle("bottom"),
8228                 "z-index" : this.getStyle("z-index")
8229             };
8230         },
8231
8232         /**
8233          * Gets the width of the border(s) for the specified side(s)
8234          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8235          * passing lr would get the border (l)eft width + the border (r)ight width.
8236          * @return {Number} The width of the sides passed added together
8237          */
8238         getBorderWidth : function(side){
8239             return this.addStyles(side, El.borders);
8240         },
8241
8242         /**
8243          * Gets the width of the padding(s) for the specified side(s)
8244          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8245          * passing lr would get the padding (l)eft + the padding (r)ight.
8246          * @return {Number} The padding of the sides passed added together
8247          */
8248         getPadding : function(side){
8249             return this.addStyles(side, El.paddings);
8250         },
8251
8252         /**
8253         * Set positioning with an object returned by getPositioning().
8254         * @param {Object} posCfg
8255         * @return {Roo.Element} this
8256          */
8257         setPositioning : function(pc){
8258             this.applyStyles(pc);
8259             if(pc.right == "auto"){
8260                 this.dom.style.right = "";
8261             }
8262             if(pc.bottom == "auto"){
8263                 this.dom.style.bottom = "";
8264             }
8265             return this;
8266         },
8267
8268         // private
8269         fixDisplay : function(){
8270             if(this.getStyle("display") == "none"){
8271                 this.setStyle("visibility", "hidden");
8272                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8273                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8274                     this.setStyle("display", "block");
8275                 }
8276             }
8277         },
8278
8279         /**
8280          * Quick set left and top adding default units
8281          * @param {String} left The left CSS property value
8282          * @param {String} top The top CSS property value
8283          * @return {Roo.Element} this
8284          */
8285          setLeftTop : function(left, top){
8286             this.dom.style.left = this.addUnits(left);
8287             this.dom.style.top = this.addUnits(top);
8288             return this;
8289         },
8290
8291         /**
8292          * Move this element relative to its current position.
8293          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8294          * @param {Number} distance How far to move the element in pixels
8295          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8296          * @return {Roo.Element} this
8297          */
8298          move : function(direction, distance, animate){
8299             var xy = this.getXY();
8300             direction = direction.toLowerCase();
8301             switch(direction){
8302                 case "l":
8303                 case "left":
8304                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8305                     break;
8306                case "r":
8307                case "right":
8308                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8309                     break;
8310                case "t":
8311                case "top":
8312                case "up":
8313                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8314                     break;
8315                case "b":
8316                case "bottom":
8317                case "down":
8318                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8319                     break;
8320             }
8321             return this;
8322         },
8323
8324         /**
8325          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8326          * @return {Roo.Element} this
8327          */
8328         clip : function(){
8329             if(!this.isClipped){
8330                this.isClipped = true;
8331                this.originalClip = {
8332                    "o": this.getStyle("overflow"),
8333                    "x": this.getStyle("overflow-x"),
8334                    "y": this.getStyle("overflow-y")
8335                };
8336                this.setStyle("overflow", "hidden");
8337                this.setStyle("overflow-x", "hidden");
8338                this.setStyle("overflow-y", "hidden");
8339             }
8340             return this;
8341         },
8342
8343         /**
8344          *  Return clipping (overflow) to original clipping before clip() was called
8345          * @return {Roo.Element} this
8346          */
8347         unclip : function(){
8348             if(this.isClipped){
8349                 this.isClipped = false;
8350                 var o = this.originalClip;
8351                 if(o.o){this.setStyle("overflow", o.o);}
8352                 if(o.x){this.setStyle("overflow-x", o.x);}
8353                 if(o.y){this.setStyle("overflow-y", o.y);}
8354             }
8355             return this;
8356         },
8357
8358
8359         /**
8360          * Gets the x,y coordinates specified by the anchor position on the element.
8361          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8362          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8363          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8364          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8365          * @return {Array} [x, y] An array containing the element's x and y coordinates
8366          */
8367         getAnchorXY : function(anchor, local, s){
8368             //Passing a different size is useful for pre-calculating anchors,
8369             //especially for anchored animations that change the el size.
8370
8371             var w, h, vp = false;
8372             if(!s){
8373                 var d = this.dom;
8374                 if(d == document.body || d == document){
8375                     vp = true;
8376                     w = D.getViewWidth(); h = D.getViewHeight();
8377                 }else{
8378                     w = this.getWidth(); h = this.getHeight();
8379                 }
8380             }else{
8381                 w = s.width;  h = s.height;
8382             }
8383             var x = 0, y = 0, r = Math.round;
8384             switch((anchor || "tl").toLowerCase()){
8385                 case "c":
8386                     x = r(w*.5);
8387                     y = r(h*.5);
8388                 break;
8389                 case "t":
8390                     x = r(w*.5);
8391                     y = 0;
8392                 break;
8393                 case "l":
8394                     x = 0;
8395                     y = r(h*.5);
8396                 break;
8397                 case "r":
8398                     x = w;
8399                     y = r(h*.5);
8400                 break;
8401                 case "b":
8402                     x = r(w*.5);
8403                     y = h;
8404                 break;
8405                 case "tl":
8406                     x = 0;
8407                     y = 0;
8408                 break;
8409                 case "bl":
8410                     x = 0;
8411                     y = h;
8412                 break;
8413                 case "br":
8414                     x = w;
8415                     y = h;
8416                 break;
8417                 case "tr":
8418                     x = w;
8419                     y = 0;
8420                 break;
8421             }
8422             if(local === true){
8423                 return [x, y];
8424             }
8425             if(vp){
8426                 var sc = this.getScroll();
8427                 return [x + sc.left, y + sc.top];
8428             }
8429             //Add the element's offset xy
8430             var o = this.getXY();
8431             return [x+o[0], y+o[1]];
8432         },
8433
8434         /**
8435          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8436          * supported position values.
8437          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8438          * @param {String} position The position to align to.
8439          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8440          * @return {Array} [x, y]
8441          */
8442         getAlignToXY : function(el, p, o){
8443             el = Roo.get(el);
8444             var d = this.dom;
8445             if(!el.dom){
8446                 throw "Element.alignTo with an element that doesn't exist";
8447             }
8448             var c = false; //constrain to viewport
8449             var p1 = "", p2 = "";
8450             o = o || [0,0];
8451
8452             if(!p){
8453                 p = "tl-bl";
8454             }else if(p == "?"){
8455                 p = "tl-bl?";
8456             }else if(p.indexOf("-") == -1){
8457                 p = "tl-" + p;
8458             }
8459             p = p.toLowerCase();
8460             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8461             if(!m){
8462                throw "Element.alignTo with an invalid alignment " + p;
8463             }
8464             p1 = m[1]; p2 = m[2]; c = !!m[3];
8465
8466             //Subtract the aligned el's internal xy from the target's offset xy
8467             //plus custom offset to get the aligned el's new offset xy
8468             var a1 = this.getAnchorXY(p1, true);
8469             var a2 = el.getAnchorXY(p2, false);
8470             var x = a2[0] - a1[0] + o[0];
8471             var y = a2[1] - a1[1] + o[1];
8472             if(c){
8473                 //constrain the aligned el to viewport if necessary
8474                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8475                 // 5px of margin for ie
8476                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8477
8478                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8479                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8480                 //otherwise swap the aligned el to the opposite border of the target.
8481                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8482                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8483                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8484                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8485
8486                var doc = document;
8487                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8488                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8489
8490                if((x+w) > dw + scrollX){
8491                     x = swapX ? r.left-w : dw+scrollX-w;
8492                 }
8493                if(x < scrollX){
8494                    x = swapX ? r.right : scrollX;
8495                }
8496                if((y+h) > dh + scrollY){
8497                     y = swapY ? r.top-h : dh+scrollY-h;
8498                 }
8499                if (y < scrollY){
8500                    y = swapY ? r.bottom : scrollY;
8501                }
8502             }
8503             return [x,y];
8504         },
8505
8506         // private
8507         getConstrainToXY : function(){
8508             var os = {top:0, left:0, bottom:0, right: 0};
8509
8510             return function(el, local, offsets, proposedXY){
8511                 el = Roo.get(el);
8512                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8513
8514                 var vw, vh, vx = 0, vy = 0;
8515                 if(el.dom == document.body || el.dom == document){
8516                     vw = Roo.lib.Dom.getViewWidth();
8517                     vh = Roo.lib.Dom.getViewHeight();
8518                 }else{
8519                     vw = el.dom.clientWidth;
8520                     vh = el.dom.clientHeight;
8521                     if(!local){
8522                         var vxy = el.getXY();
8523                         vx = vxy[0];
8524                         vy = vxy[1];
8525                     }
8526                 }
8527
8528                 var s = el.getScroll();
8529
8530                 vx += offsets.left + s.left;
8531                 vy += offsets.top + s.top;
8532
8533                 vw -= offsets.right;
8534                 vh -= offsets.bottom;
8535
8536                 var vr = vx+vw;
8537                 var vb = vy+vh;
8538
8539                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8540                 var x = xy[0], y = xy[1];
8541                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8542
8543                 // only move it if it needs it
8544                 var moved = false;
8545
8546                 // first validate right/bottom
8547                 if((x + w) > vr){
8548                     x = vr - w;
8549                     moved = true;
8550                 }
8551                 if((y + h) > vb){
8552                     y = vb - h;
8553                     moved = true;
8554                 }
8555                 // then make sure top/left isn't negative
8556                 if(x < vx){
8557                     x = vx;
8558                     moved = true;
8559                 }
8560                 if(y < vy){
8561                     y = vy;
8562                     moved = true;
8563                 }
8564                 return moved ? [x, y] : false;
8565             };
8566         }(),
8567
8568         // private
8569         adjustForConstraints : function(xy, parent, offsets){
8570             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8571         },
8572
8573         /**
8574          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8575          * document it aligns it to the viewport.
8576          * The position parameter is optional, and can be specified in any one of the following formats:
8577          * <ul>
8578          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8579          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8580          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8581          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8582          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8583          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8584          * </ul>
8585          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8586          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8587          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8588          * that specified in order to enforce the viewport constraints.
8589          * Following are all of the supported anchor positions:
8590     <pre>
8591     Value  Description
8592     -----  -----------------------------
8593     tl     The top left corner (default)
8594     t      The center of the top edge
8595     tr     The top right corner
8596     l      The center of the left edge
8597     c      In the center of the element
8598     r      The center of the right edge
8599     bl     The bottom left corner
8600     b      The center of the bottom edge
8601     br     The bottom right corner
8602     </pre>
8603     Example Usage:
8604     <pre><code>
8605     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8606     el.alignTo("other-el");
8607
8608     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8609     el.alignTo("other-el", "tr?");
8610
8611     // align the bottom right corner of el with the center left edge of other-el
8612     el.alignTo("other-el", "br-l?");
8613
8614     // align the center of el with the bottom left corner of other-el and
8615     // adjust the x position by -6 pixels (and the y position by 0)
8616     el.alignTo("other-el", "c-bl", [-6, 0]);
8617     </code></pre>
8618          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8619          * @param {String} position The position to align to.
8620          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8621          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8622          * @return {Roo.Element} this
8623          */
8624         alignTo : function(element, position, offsets, animate){
8625             var xy = this.getAlignToXY(element, position, offsets);
8626             this.setXY(xy, this.preanim(arguments, 3));
8627             return this;
8628         },
8629
8630         /**
8631          * Anchors an element to another element and realigns it when the window is resized.
8632          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8633          * @param {String} position The position to align to.
8634          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8635          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8636          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8637          * is a number, it is used as the buffer delay (defaults to 50ms).
8638          * @param {Function} callback The function to call after the animation finishes
8639          * @return {Roo.Element} this
8640          */
8641         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8642             var action = function(){
8643                 this.alignTo(el, alignment, offsets, animate);
8644                 Roo.callback(callback, this);
8645             };
8646             Roo.EventManager.onWindowResize(action, this);
8647             var tm = typeof monitorScroll;
8648             if(tm != 'undefined'){
8649                 Roo.EventManager.on(window, 'scroll', action, this,
8650                     {buffer: tm == 'number' ? monitorScroll : 50});
8651             }
8652             action.call(this); // align immediately
8653             return this;
8654         },
8655         /**
8656          * Clears any opacity settings from this element. Required in some cases for IE.
8657          * @return {Roo.Element} this
8658          */
8659         clearOpacity : function(){
8660             if (window.ActiveXObject) {
8661                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8662                     this.dom.style.filter = "";
8663                 }
8664             } else {
8665                 this.dom.style.opacity = "";
8666                 this.dom.style["-moz-opacity"] = "";
8667                 this.dom.style["-khtml-opacity"] = "";
8668             }
8669             return this;
8670         },
8671
8672         /**
8673          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8674          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8675          * @return {Roo.Element} this
8676          */
8677         hide : function(animate){
8678             this.setVisible(false, this.preanim(arguments, 0));
8679             return this;
8680         },
8681
8682         /**
8683         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8684         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8685          * @return {Roo.Element} this
8686          */
8687         show : function(animate){
8688             this.setVisible(true, this.preanim(arguments, 0));
8689             return this;
8690         },
8691
8692         /**
8693          * @private Test if size has a unit, otherwise appends the default
8694          */
8695         addUnits : function(size){
8696             return Roo.Element.addUnits(size, this.defaultUnit);
8697         },
8698
8699         /**
8700          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8701          * @return {Roo.Element} this
8702          */
8703         beginMeasure : function(){
8704             var el = this.dom;
8705             if(el.offsetWidth || el.offsetHeight){
8706                 return this; // offsets work already
8707             }
8708             var changed = [];
8709             var p = this.dom, b = document.body; // start with this element
8710             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8711                 var pe = Roo.get(p);
8712                 if(pe.getStyle('display') == 'none'){
8713                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8714                     p.style.visibility = "hidden";
8715                     p.style.display = "block";
8716                 }
8717                 p = p.parentNode;
8718             }
8719             this._measureChanged = changed;
8720             return this;
8721
8722         },
8723
8724         /**
8725          * Restores displays to before beginMeasure was called
8726          * @return {Roo.Element} this
8727          */
8728         endMeasure : function(){
8729             var changed = this._measureChanged;
8730             if(changed){
8731                 for(var i = 0, len = changed.length; i < len; i++) {
8732                     var r = changed[i];
8733                     r.el.style.visibility = r.visibility;
8734                     r.el.style.display = "none";
8735                 }
8736                 this._measureChanged = null;
8737             }
8738             return this;
8739         },
8740
8741         /**
8742         * Update the innerHTML of this element, optionally searching for and processing scripts
8743         * @param {String} html The new HTML
8744         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8745         * @param {Function} callback For async script loading you can be noticed when the update completes
8746         * @return {Roo.Element} this
8747          */
8748         update : function(html, loadScripts, callback){
8749             if(typeof html == "undefined"){
8750                 html = "";
8751             }
8752             if(loadScripts !== true){
8753                 this.dom.innerHTML = html;
8754                 if(typeof callback == "function"){
8755                     callback();
8756                 }
8757                 return this;
8758             }
8759             var id = Roo.id();
8760             var dom = this.dom;
8761
8762             html += '<span id="' + id + '"></span>';
8763
8764             E.onAvailable(id, function(){
8765                 var hd = document.getElementsByTagName("head")[0];
8766                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8767                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8768                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8769
8770                 var match;
8771                 while(match = re.exec(html)){
8772                     var attrs = match[1];
8773                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8774                     if(srcMatch && srcMatch[2]){
8775                        var s = document.createElement("script");
8776                        s.src = srcMatch[2];
8777                        var typeMatch = attrs.match(typeRe);
8778                        if(typeMatch && typeMatch[2]){
8779                            s.type = typeMatch[2];
8780                        }
8781                        hd.appendChild(s);
8782                     }else if(match[2] && match[2].length > 0){
8783                         if(window.execScript) {
8784                            window.execScript(match[2]);
8785                         } else {
8786                             /**
8787                              * eval:var:id
8788                              * eval:var:dom
8789                              * eval:var:html
8790                              * 
8791                              */
8792                            window.eval(match[2]);
8793                         }
8794                     }
8795                 }
8796                 var el = document.getElementById(id);
8797                 if(el){el.parentNode.removeChild(el);}
8798                 if(typeof callback == "function"){
8799                     callback();
8800                 }
8801             });
8802             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8803             return this;
8804         },
8805
8806         /**
8807          * Direct access to the UpdateManager update() method (takes the same parameters).
8808          * @param {String/Function} url The url for this request or a function to call to get the url
8809          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8810          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8811          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8812          * @return {Roo.Element} this
8813          */
8814         load : function(){
8815             var um = this.getUpdateManager();
8816             um.update.apply(um, arguments);
8817             return this;
8818         },
8819
8820         /**
8821         * Gets this element's UpdateManager
8822         * @return {Roo.UpdateManager} The UpdateManager
8823         */
8824         getUpdateManager : function(){
8825             if(!this.updateManager){
8826                 this.updateManager = new Roo.UpdateManager(this);
8827             }
8828             return this.updateManager;
8829         },
8830
8831         /**
8832          * Disables text selection for this element (normalized across browsers)
8833          * @return {Roo.Element} this
8834          */
8835         unselectable : function(){
8836             this.dom.unselectable = "on";
8837             this.swallowEvent("selectstart", true);
8838             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8839             this.addClass("x-unselectable");
8840             return this;
8841         },
8842
8843         /**
8844         * Calculates the x, y to center this element on the screen
8845         * @return {Array} The x, y values [x, y]
8846         */
8847         getCenterXY : function(){
8848             return this.getAlignToXY(document, 'c-c');
8849         },
8850
8851         /**
8852         * Centers the Element in either the viewport, or another Element.
8853         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8854         */
8855         center : function(centerIn){
8856             this.alignTo(centerIn || document, 'c-c');
8857             return this;
8858         },
8859
8860         /**
8861          * Tests various css rules/browsers to determine if this element uses a border box
8862          * @return {Boolean}
8863          */
8864         isBorderBox : function(){
8865             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8866         },
8867
8868         /**
8869          * Return a box {x, y, width, height} that can be used to set another elements
8870          * size/location to match this element.
8871          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8872          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8873          * @return {Object} box An object in the format {x, y, width, height}
8874          */
8875         getBox : function(contentBox, local){
8876             var xy;
8877             if(!local){
8878                 xy = this.getXY();
8879             }else{
8880                 var left = parseInt(this.getStyle("left"), 10) || 0;
8881                 var top = parseInt(this.getStyle("top"), 10) || 0;
8882                 xy = [left, top];
8883             }
8884             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8885             if(!contentBox){
8886                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8887             }else{
8888                 var l = this.getBorderWidth("l")+this.getPadding("l");
8889                 var r = this.getBorderWidth("r")+this.getPadding("r");
8890                 var t = this.getBorderWidth("t")+this.getPadding("t");
8891                 var b = this.getBorderWidth("b")+this.getPadding("b");
8892                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8893             }
8894             bx.right = bx.x + bx.width;
8895             bx.bottom = bx.y + bx.height;
8896             return bx;
8897         },
8898
8899         /**
8900          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8901          for more information about the sides.
8902          * @param {String} sides
8903          * @return {Number}
8904          */
8905         getFrameWidth : function(sides, onlyContentBox){
8906             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8907         },
8908
8909         /**
8910          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8911          * @param {Object} box The box to fill {x, y, width, height}
8912          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8913          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8914          * @return {Roo.Element} this
8915          */
8916         setBox : function(box, adjust, animate){
8917             var w = box.width, h = box.height;
8918             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8919                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8920                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8921             }
8922             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8923             return this;
8924         },
8925
8926         /**
8927          * Forces the browser to repaint this element
8928          * @return {Roo.Element} this
8929          */
8930          repaint : function(){
8931             var dom = this.dom;
8932             this.addClass("x-repaint");
8933             setTimeout(function(){
8934                 Roo.get(dom).removeClass("x-repaint");
8935             }, 1);
8936             return this;
8937         },
8938
8939         /**
8940          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8941          * then it returns the calculated width of the sides (see getPadding)
8942          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8943          * @return {Object/Number}
8944          */
8945         getMargins : function(side){
8946             if(!side){
8947                 return {
8948                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8949                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8950                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8951                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8952                 };
8953             }else{
8954                 return this.addStyles(side, El.margins);
8955              }
8956         },
8957
8958         // private
8959         addStyles : function(sides, styles){
8960             var val = 0, v, w;
8961             for(var i = 0, len = sides.length; i < len; i++){
8962                 v = this.getStyle(styles[sides.charAt(i)]);
8963                 if(v){
8964                      w = parseInt(v, 10);
8965                      if(w){ val += w; }
8966                 }
8967             }
8968             return val;
8969         },
8970
8971         /**
8972          * Creates a proxy element of this element
8973          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8974          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8975          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8976          * @return {Roo.Element} The new proxy element
8977          */
8978         createProxy : function(config, renderTo, matchBox){
8979             if(renderTo){
8980                 renderTo = Roo.getDom(renderTo);
8981             }else{
8982                 renderTo = document.body;
8983             }
8984             config = typeof config == "object" ?
8985                 config : {tag : "div", cls: config};
8986             var proxy = Roo.DomHelper.append(renderTo, config, true);
8987             if(matchBox){
8988                proxy.setBox(this.getBox());
8989             }
8990             return proxy;
8991         },
8992
8993         /**
8994          * Puts a mask over this element to disable user interaction. Requires core.css.
8995          * This method can only be applied to elements which accept child nodes.
8996          * @param {String} msg (optional) A message to display in the mask
8997          * @param {String} msgCls (optional) A css class to apply to the msg element
8998          * @return {Element} The mask  element
8999          */
9000         mask : function(msg, msgCls)
9001         {
9002             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9003                 this.setStyle("position", "relative");
9004             }
9005             if(!this._mask){
9006                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9007             }
9008             this.addClass("x-masked");
9009             this._mask.setDisplayed(true);
9010             
9011             // we wander
9012             var z = 0;
9013             var dom = this.dom;
9014             while (dom && dom.style) {
9015                 if (!isNaN(parseInt(dom.style.zIndex))) {
9016                     z = Math.max(z, parseInt(dom.style.zIndex));
9017                 }
9018                 dom = dom.parentNode;
9019             }
9020             // if we are masking the body - then it hides everything..
9021             if (this.dom == document.body) {
9022                 z = 1000000;
9023                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9024                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9025             }
9026            
9027             if(typeof msg == 'string'){
9028                 if(!this._maskMsg){
9029                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9030                 }
9031                 var mm = this._maskMsg;
9032                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9033                 if (mm.dom.firstChild) { // weird IE issue?
9034                     mm.dom.firstChild.innerHTML = msg;
9035                 }
9036                 mm.setDisplayed(true);
9037                 mm.center(this);
9038                 mm.setStyle('z-index', z + 102);
9039             }
9040             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9041                 this._mask.setHeight(this.getHeight());
9042             }
9043             this._mask.setStyle('z-index', z + 100);
9044             
9045             return this._mask;
9046         },
9047
9048         /**
9049          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9050          * it is cached for reuse.
9051          */
9052         unmask : function(removeEl){
9053             if(this._mask){
9054                 if(removeEl === true){
9055                     this._mask.remove();
9056                     delete this._mask;
9057                     if(this._maskMsg){
9058                         this._maskMsg.remove();
9059                         delete this._maskMsg;
9060                     }
9061                 }else{
9062                     this._mask.setDisplayed(false);
9063                     if(this._maskMsg){
9064                         this._maskMsg.setDisplayed(false);
9065                     }
9066                 }
9067             }
9068             this.removeClass("x-masked");
9069         },
9070
9071         /**
9072          * Returns true if this element is masked
9073          * @return {Boolean}
9074          */
9075         isMasked : function(){
9076             return this._mask && this._mask.isVisible();
9077         },
9078
9079         /**
9080          * Creates an iframe shim for this element to keep selects and other windowed objects from
9081          * showing through.
9082          * @return {Roo.Element} The new shim element
9083          */
9084         createShim : function(){
9085             var el = document.createElement('iframe');
9086             el.frameBorder = 'no';
9087             el.className = 'roo-shim';
9088             if(Roo.isIE && Roo.isSecure){
9089                 el.src = Roo.SSL_SECURE_URL;
9090             }
9091             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9092             shim.autoBoxAdjust = false;
9093             return shim;
9094         },
9095
9096         /**
9097          * Removes this element from the DOM and deletes it from the cache
9098          */
9099         remove : function(){
9100             if(this.dom.parentNode){
9101                 this.dom.parentNode.removeChild(this.dom);
9102             }
9103             delete El.cache[this.dom.id];
9104         },
9105
9106         /**
9107          * Sets up event handlers to add and remove a css class when the mouse is over this element
9108          * @param {String} className
9109          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9110          * mouseout events for children elements
9111          * @return {Roo.Element} this
9112          */
9113         addClassOnOver : function(className, preventFlicker){
9114             this.on("mouseover", function(){
9115                 Roo.fly(this, '_internal').addClass(className);
9116             }, this.dom);
9117             var removeFn = function(e){
9118                 if(preventFlicker !== true || !e.within(this, true)){
9119                     Roo.fly(this, '_internal').removeClass(className);
9120                 }
9121             };
9122             this.on("mouseout", removeFn, this.dom);
9123             return this;
9124         },
9125
9126         /**
9127          * Sets up event handlers to add and remove a css class when this element has the focus
9128          * @param {String} className
9129          * @return {Roo.Element} this
9130          */
9131         addClassOnFocus : function(className){
9132             this.on("focus", function(){
9133                 Roo.fly(this, '_internal').addClass(className);
9134             }, this.dom);
9135             this.on("blur", function(){
9136                 Roo.fly(this, '_internal').removeClass(className);
9137             }, this.dom);
9138             return this;
9139         },
9140         /**
9141          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9142          * @param {String} className
9143          * @return {Roo.Element} this
9144          */
9145         addClassOnClick : function(className){
9146             var dom = this.dom;
9147             this.on("mousedown", function(){
9148                 Roo.fly(dom, '_internal').addClass(className);
9149                 var d = Roo.get(document);
9150                 var fn = function(){
9151                     Roo.fly(dom, '_internal').removeClass(className);
9152                     d.removeListener("mouseup", fn);
9153                 };
9154                 d.on("mouseup", fn);
9155             });
9156             return this;
9157         },
9158
9159         /**
9160          * Stops the specified event from bubbling and optionally prevents the default action
9161          * @param {String} eventName
9162          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9163          * @return {Roo.Element} this
9164          */
9165         swallowEvent : function(eventName, preventDefault){
9166             var fn = function(e){
9167                 e.stopPropagation();
9168                 if(preventDefault){
9169                     e.preventDefault();
9170                 }
9171             };
9172             if(eventName instanceof Array){
9173                 for(var i = 0, len = eventName.length; i < len; i++){
9174                      this.on(eventName[i], fn);
9175                 }
9176                 return this;
9177             }
9178             this.on(eventName, fn);
9179             return this;
9180         },
9181
9182         /**
9183          * @private
9184          */
9185       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9186
9187         /**
9188          * Sizes this element to its parent element's dimensions performing
9189          * neccessary box adjustments.
9190          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9191          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9192          * @return {Roo.Element} this
9193          */
9194         fitToParent : function(monitorResize, targetParent) {
9195           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9196           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9197           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9198             return;
9199           }
9200           var p = Roo.get(targetParent || this.dom.parentNode);
9201           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9202           if (monitorResize === true) {
9203             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9204             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9205           }
9206           return this;
9207         },
9208
9209         /**
9210          * Gets the next sibling, skipping text nodes
9211          * @return {HTMLElement} The next sibling or null
9212          */
9213         getNextSibling : function(){
9214             var n = this.dom.nextSibling;
9215             while(n && n.nodeType != 1){
9216                 n = n.nextSibling;
9217             }
9218             return n;
9219         },
9220
9221         /**
9222          * Gets the previous sibling, skipping text nodes
9223          * @return {HTMLElement} The previous sibling or null
9224          */
9225         getPrevSibling : function(){
9226             var n = this.dom.previousSibling;
9227             while(n && n.nodeType != 1){
9228                 n = n.previousSibling;
9229             }
9230             return n;
9231         },
9232
9233
9234         /**
9235          * Appends the passed element(s) to this element
9236          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9237          * @return {Roo.Element} this
9238          */
9239         appendChild: function(el){
9240             el = Roo.get(el);
9241             el.appendTo(this);
9242             return this;
9243         },
9244
9245         /**
9246          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9247          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9248          * automatically generated with the specified attributes.
9249          * @param {HTMLElement} insertBefore (optional) a child element of this element
9250          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9251          * @return {Roo.Element} The new child element
9252          */
9253         createChild: function(config, insertBefore, returnDom){
9254             config = config || {tag:'div'};
9255             if(insertBefore){
9256                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9257             }
9258             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9259         },
9260
9261         /**
9262          * Appends this element to the passed element
9263          * @param {String/HTMLElement/Element} el The new parent element
9264          * @return {Roo.Element} this
9265          */
9266         appendTo: function(el){
9267             el = Roo.getDom(el);
9268             el.appendChild(this.dom);
9269             return this;
9270         },
9271
9272         /**
9273          * Inserts this element before the passed element in the DOM
9274          * @param {String/HTMLElement/Element} el The element to insert before
9275          * @return {Roo.Element} this
9276          */
9277         insertBefore: function(el){
9278             el = Roo.getDom(el);
9279             el.parentNode.insertBefore(this.dom, el);
9280             return this;
9281         },
9282
9283         /**
9284          * Inserts this element after the passed element in the DOM
9285          * @param {String/HTMLElement/Element} el The element to insert after
9286          * @return {Roo.Element} this
9287          */
9288         insertAfter: function(el){
9289             el = Roo.getDom(el);
9290             el.parentNode.insertBefore(this.dom, el.nextSibling);
9291             return this;
9292         },
9293
9294         /**
9295          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9296          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9297          * @return {Roo.Element} The new child
9298          */
9299         insertFirst: function(el, returnDom){
9300             el = el || {};
9301             if(typeof el == 'object' && !el.nodeType){ // dh config
9302                 return this.createChild(el, this.dom.firstChild, returnDom);
9303             }else{
9304                 el = Roo.getDom(el);
9305                 this.dom.insertBefore(el, this.dom.firstChild);
9306                 return !returnDom ? Roo.get(el) : el;
9307             }
9308         },
9309
9310         /**
9311          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9312          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9313          * @param {String} where (optional) 'before' or 'after' defaults to before
9314          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9315          * @return {Roo.Element} the inserted Element
9316          */
9317         insertSibling: function(el, where, returnDom){
9318             where = where ? where.toLowerCase() : 'before';
9319             el = el || {};
9320             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9321
9322             if(typeof el == 'object' && !el.nodeType){ // dh config
9323                 if(where == 'after' && !this.dom.nextSibling){
9324                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9325                 }else{
9326                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9327                 }
9328
9329             }else{
9330                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9331                             where == 'before' ? this.dom : this.dom.nextSibling);
9332                 if(!returnDom){
9333                     rt = Roo.get(rt);
9334                 }
9335             }
9336             return rt;
9337         },
9338
9339         /**
9340          * Creates and wraps this element with another element
9341          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9342          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9343          * @return {HTMLElement/Element} The newly created wrapper element
9344          */
9345         wrap: function(config, returnDom){
9346             if(!config){
9347                 config = {tag: "div"};
9348             }
9349             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9350             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9351             return newEl;
9352         },
9353
9354         /**
9355          * Replaces the passed element with this element
9356          * @param {String/HTMLElement/Element} el The element to replace
9357          * @return {Roo.Element} this
9358          */
9359         replace: function(el){
9360             el = Roo.get(el);
9361             this.insertBefore(el);
9362             el.remove();
9363             return this;
9364         },
9365
9366         /**
9367          * Inserts an html fragment into this element
9368          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9369          * @param {String} html The HTML fragment
9370          * @param {Boolean} returnEl True to return an Roo.Element
9371          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9372          */
9373         insertHtml : function(where, html, returnEl){
9374             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9375             return returnEl ? Roo.get(el) : el;
9376         },
9377
9378         /**
9379          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9380          * @param {Object} o The object with the attributes
9381          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9382          * @return {Roo.Element} this
9383          */
9384         set : function(o, useSet){
9385             var el = this.dom;
9386             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9387             for(var attr in o){
9388                 if(attr == "style" || typeof o[attr] == "function") continue;
9389                 if(attr=="cls"){
9390                     el.className = o["cls"];
9391                 }else{
9392                     if(useSet) el.setAttribute(attr, o[attr]);
9393                     else el[attr] = o[attr];
9394                 }
9395             }
9396             if(o.style){
9397                 Roo.DomHelper.applyStyles(el, o.style);
9398             }
9399             return this;
9400         },
9401
9402         /**
9403          * Convenience method for constructing a KeyMap
9404          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9405          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9406          * @param {Function} fn The function to call
9407          * @param {Object} scope (optional) The scope of the function
9408          * @return {Roo.KeyMap} The KeyMap created
9409          */
9410         addKeyListener : function(key, fn, scope){
9411             var config;
9412             if(typeof key != "object" || key instanceof Array){
9413                 config = {
9414                     key: key,
9415                     fn: fn,
9416                     scope: scope
9417                 };
9418             }else{
9419                 config = {
9420                     key : key.key,
9421                     shift : key.shift,
9422                     ctrl : key.ctrl,
9423                     alt : key.alt,
9424                     fn: fn,
9425                     scope: scope
9426                 };
9427             }
9428             return new Roo.KeyMap(this, config);
9429         },
9430
9431         /**
9432          * Creates a KeyMap for this element
9433          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9434          * @return {Roo.KeyMap} The KeyMap created
9435          */
9436         addKeyMap : function(config){
9437             return new Roo.KeyMap(this, config);
9438         },
9439
9440         /**
9441          * Returns true if this element is scrollable.
9442          * @return {Boolean}
9443          */
9444          isScrollable : function(){
9445             var dom = this.dom;
9446             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9447         },
9448
9449         /**
9450          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9451          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9452          * @param {Number} value The new scroll value
9453          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9454          * @return {Element} this
9455          */
9456
9457         scrollTo : function(side, value, animate){
9458             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9459             if(!animate || !A){
9460                 this.dom[prop] = value;
9461             }else{
9462                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9463                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9464             }
9465             return this;
9466         },
9467
9468         /**
9469          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9470          * within this element's scrollable range.
9471          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9472          * @param {Number} distance How far to scroll the element in pixels
9473          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9474          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9475          * was scrolled as far as it could go.
9476          */
9477          scroll : function(direction, distance, animate){
9478              if(!this.isScrollable()){
9479                  return;
9480              }
9481              var el = this.dom;
9482              var l = el.scrollLeft, t = el.scrollTop;
9483              var w = el.scrollWidth, h = el.scrollHeight;
9484              var cw = el.clientWidth, ch = el.clientHeight;
9485              direction = direction.toLowerCase();
9486              var scrolled = false;
9487              var a = this.preanim(arguments, 2);
9488              switch(direction){
9489                  case "l":
9490                  case "left":
9491                      if(w - l > cw){
9492                          var v = Math.min(l + distance, w-cw);
9493                          this.scrollTo("left", v, a);
9494                          scrolled = true;
9495                      }
9496                      break;
9497                 case "r":
9498                 case "right":
9499                      if(l > 0){
9500                          var v = Math.max(l - distance, 0);
9501                          this.scrollTo("left", v, a);
9502                          scrolled = true;
9503                      }
9504                      break;
9505                 case "t":
9506                 case "top":
9507                 case "up":
9508                      if(t > 0){
9509                          var v = Math.max(t - distance, 0);
9510                          this.scrollTo("top", v, a);
9511                          scrolled = true;
9512                      }
9513                      break;
9514                 case "b":
9515                 case "bottom":
9516                 case "down":
9517                      if(h - t > ch){
9518                          var v = Math.min(t + distance, h-ch);
9519                          this.scrollTo("top", v, a);
9520                          scrolled = true;
9521                      }
9522                      break;
9523              }
9524              return scrolled;
9525         },
9526
9527         /**
9528          * Translates the passed page coordinates into left/top css values for this element
9529          * @param {Number/Array} x The page x or an array containing [x, y]
9530          * @param {Number} y The page y
9531          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9532          */
9533         translatePoints : function(x, y){
9534             if(typeof x == 'object' || x instanceof Array){
9535                 y = x[1]; x = x[0];
9536             }
9537             var p = this.getStyle('position');
9538             var o = this.getXY();
9539
9540             var l = parseInt(this.getStyle('left'), 10);
9541             var t = parseInt(this.getStyle('top'), 10);
9542
9543             if(isNaN(l)){
9544                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9545             }
9546             if(isNaN(t)){
9547                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9548             }
9549
9550             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9551         },
9552
9553         /**
9554          * Returns the current scroll position of the element.
9555          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9556          */
9557         getScroll : function(){
9558             var d = this.dom, doc = document;
9559             if(d == doc || d == doc.body){
9560                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9561                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9562                 return {left: l, top: t};
9563             }else{
9564                 return {left: d.scrollLeft, top: d.scrollTop};
9565             }
9566         },
9567
9568         /**
9569          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9570          * are convert to standard 6 digit hex color.
9571          * @param {String} attr The css attribute
9572          * @param {String} defaultValue The default value to use when a valid color isn't found
9573          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9574          * YUI color anims.
9575          */
9576         getColor : function(attr, defaultValue, prefix){
9577             var v = this.getStyle(attr);
9578             if(!v || v == "transparent" || v == "inherit") {
9579                 return defaultValue;
9580             }
9581             var color = typeof prefix == "undefined" ? "#" : prefix;
9582             if(v.substr(0, 4) == "rgb("){
9583                 var rvs = v.slice(4, v.length -1).split(",");
9584                 for(var i = 0; i < 3; i++){
9585                     var h = parseInt(rvs[i]).toString(16);
9586                     if(h < 16){
9587                         h = "0" + h;
9588                     }
9589                     color += h;
9590                 }
9591             } else {
9592                 if(v.substr(0, 1) == "#"){
9593                     if(v.length == 4) {
9594                         for(var i = 1; i < 4; i++){
9595                             var c = v.charAt(i);
9596                             color +=  c + c;
9597                         }
9598                     }else if(v.length == 7){
9599                         color += v.substr(1);
9600                     }
9601                 }
9602             }
9603             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9604         },
9605
9606         /**
9607          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9608          * gradient background, rounded corners and a 4-way shadow.
9609          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9610          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9611          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9612          * @return {Roo.Element} this
9613          */
9614         boxWrap : function(cls){
9615             cls = cls || 'x-box';
9616             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9617             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9618             return el;
9619         },
9620
9621         /**
9622          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9623          * @param {String} namespace The namespace in which to look for the attribute
9624          * @param {String} name The attribute name
9625          * @return {String} The attribute value
9626          */
9627         getAttributeNS : Roo.isIE ? function(ns, name){
9628             var d = this.dom;
9629             var type = typeof d[ns+":"+name];
9630             if(type != 'undefined' && type != 'unknown'){
9631                 return d[ns+":"+name];
9632             }
9633             return d[name];
9634         } : function(ns, name){
9635             var d = this.dom;
9636             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9637         },
9638         
9639         
9640         /**
9641          * Sets or Returns the value the dom attribute value
9642          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9643          * @param {String} value (optional) The value to set the attribute to
9644          * @return {String} The attribute value
9645          */
9646         attr : function(name){
9647             if (arguments.length > 1) {
9648                 this.dom.setAttribute(name, arguments[1]);
9649                 return arguments[1];
9650             }
9651             if (typeof(name) == 'object') {
9652                 for(var i in name) {
9653                     this.attr(i, name[i]);
9654                 }
9655                 return name;
9656             }
9657             
9658             
9659             if (!this.dom.hasAttribute(name)) {
9660                 return undefined;
9661             }
9662             return this.dom.getAttribute(name);
9663         }
9664         
9665         
9666         
9667     };
9668
9669     var ep = El.prototype;
9670
9671     /**
9672      * Appends an event handler (Shorthand for addListener)
9673      * @param {String}   eventName     The type of event to append
9674      * @param {Function} fn        The method the event invokes
9675      * @param {Object} scope       (optional) The scope (this object) of the fn
9676      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9677      * @method
9678      */
9679     ep.on = ep.addListener;
9680         // backwards compat
9681     ep.mon = ep.addListener;
9682
9683     /**
9684      * Removes an event handler from this element (shorthand for removeListener)
9685      * @param {String} eventName the type of event to remove
9686      * @param {Function} fn the method the event invokes
9687      * @return {Roo.Element} this
9688      * @method
9689      */
9690     ep.un = ep.removeListener;
9691
9692     /**
9693      * true to automatically adjust width and height settings for box-model issues (default to true)
9694      */
9695     ep.autoBoxAdjust = true;
9696
9697     // private
9698     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9699
9700     // private
9701     El.addUnits = function(v, defaultUnit){
9702         if(v === "" || v == "auto"){
9703             return v;
9704         }
9705         if(v === undefined){
9706             return '';
9707         }
9708         if(typeof v == "number" || !El.unitPattern.test(v)){
9709             return v + (defaultUnit || 'px');
9710         }
9711         return v;
9712     };
9713
9714     // special markup used throughout Roo when box wrapping elements
9715     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9716     /**
9717      * Visibility mode constant - Use visibility to hide element
9718      * @static
9719      * @type Number
9720      */
9721     El.VISIBILITY = 1;
9722     /**
9723      * Visibility mode constant - Use display to hide element
9724      * @static
9725      * @type Number
9726      */
9727     El.DISPLAY = 2;
9728
9729     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9730     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9731     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9732
9733
9734
9735     /**
9736      * @private
9737      */
9738     El.cache = {};
9739
9740     var docEl;
9741
9742     /**
9743      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9744      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9745      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9746      * @return {Element} The Element object
9747      * @static
9748      */
9749     El.get = function(el){
9750         var ex, elm, id;
9751         if(!el){ return null; }
9752         if(typeof el == "string"){ // element id
9753             if(!(elm = document.getElementById(el))){
9754                 return null;
9755             }
9756             if(ex = El.cache[el]){
9757                 ex.dom = elm;
9758             }else{
9759                 ex = El.cache[el] = new El(elm);
9760             }
9761             return ex;
9762         }else if(el.tagName){ // dom element
9763             if(!(id = el.id)){
9764                 id = Roo.id(el);
9765             }
9766             if(ex = El.cache[id]){
9767                 ex.dom = el;
9768             }else{
9769                 ex = El.cache[id] = new El(el);
9770             }
9771             return ex;
9772         }else if(el instanceof El){
9773             if(el != docEl){
9774                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9775                                                               // catch case where it hasn't been appended
9776                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9777             }
9778             return el;
9779         }else if(el.isComposite){
9780             return el;
9781         }else if(el instanceof Array){
9782             return El.select(el);
9783         }else if(el == document){
9784             // create a bogus element object representing the document object
9785             if(!docEl){
9786                 var f = function(){};
9787                 f.prototype = El.prototype;
9788                 docEl = new f();
9789                 docEl.dom = document;
9790             }
9791             return docEl;
9792         }
9793         return null;
9794     };
9795
9796     // private
9797     El.uncache = function(el){
9798         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9799             if(a[i]){
9800                 delete El.cache[a[i].id || a[i]];
9801             }
9802         }
9803     };
9804
9805     // private
9806     // Garbage collection - uncache elements/purge listeners on orphaned elements
9807     // so we don't hold a reference and cause the browser to retain them
9808     El.garbageCollect = function(){
9809         if(!Roo.enableGarbageCollector){
9810             clearInterval(El.collectorThread);
9811             return;
9812         }
9813         for(var eid in El.cache){
9814             var el = El.cache[eid], d = el.dom;
9815             // -------------------------------------------------------
9816             // Determining what is garbage:
9817             // -------------------------------------------------------
9818             // !d
9819             // dom node is null, definitely garbage
9820             // -------------------------------------------------------
9821             // !d.parentNode
9822             // no parentNode == direct orphan, definitely garbage
9823             // -------------------------------------------------------
9824             // !d.offsetParent && !document.getElementById(eid)
9825             // display none elements have no offsetParent so we will
9826             // also try to look it up by it's id. However, check
9827             // offsetParent first so we don't do unneeded lookups.
9828             // This enables collection of elements that are not orphans
9829             // directly, but somewhere up the line they have an orphan
9830             // parent.
9831             // -------------------------------------------------------
9832             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9833                 delete El.cache[eid];
9834                 if(d && Roo.enableListenerCollection){
9835                     E.purgeElement(d);
9836                 }
9837             }
9838         }
9839     }
9840     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9841
9842
9843     // dom is optional
9844     El.Flyweight = function(dom){
9845         this.dom = dom;
9846     };
9847     El.Flyweight.prototype = El.prototype;
9848
9849     El._flyweights = {};
9850     /**
9851      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9852      * the dom node can be overwritten by other code.
9853      * @param {String/HTMLElement} el The dom node or id
9854      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9855      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9856      * @static
9857      * @return {Element} The shared Element object
9858      */
9859     El.fly = function(el, named){
9860         named = named || '_global';
9861         el = Roo.getDom(el);
9862         if(!el){
9863             return null;
9864         }
9865         if(!El._flyweights[named]){
9866             El._flyweights[named] = new El.Flyweight();
9867         }
9868         El._flyweights[named].dom = el;
9869         return El._flyweights[named];
9870     };
9871
9872     /**
9873      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9874      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9875      * Shorthand of {@link Roo.Element#get}
9876      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9877      * @return {Element} The Element object
9878      * @member Roo
9879      * @method get
9880      */
9881     Roo.get = El.get;
9882     /**
9883      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9884      * the dom node can be overwritten by other code.
9885      * Shorthand of {@link Roo.Element#fly}
9886      * @param {String/HTMLElement} el The dom node or id
9887      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9888      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9889      * @static
9890      * @return {Element} The shared Element object
9891      * @member Roo
9892      * @method fly
9893      */
9894     Roo.fly = El.fly;
9895
9896     // speedy lookup for elements never to box adjust
9897     var noBoxAdjust = Roo.isStrict ? {
9898         select:1
9899     } : {
9900         input:1, select:1, textarea:1
9901     };
9902     if(Roo.isIE || Roo.isGecko){
9903         noBoxAdjust['button'] = 1;
9904     }
9905
9906
9907     Roo.EventManager.on(window, 'unload', function(){
9908         delete El.cache;
9909         delete El._flyweights;
9910     });
9911 })();
9912
9913
9914
9915
9916 if(Roo.DomQuery){
9917     Roo.Element.selectorFunction = Roo.DomQuery.select;
9918 }
9919
9920 Roo.Element.select = function(selector, unique, root){
9921     var els;
9922     if(typeof selector == "string"){
9923         els = Roo.Element.selectorFunction(selector, root);
9924     }else if(selector.length !== undefined){
9925         els = selector;
9926     }else{
9927         throw "Invalid selector";
9928     }
9929     if(unique === true){
9930         return new Roo.CompositeElement(els);
9931     }else{
9932         return new Roo.CompositeElementLite(els);
9933     }
9934 };
9935 /**
9936  * Selects elements based on the passed CSS selector to enable working on them as 1.
9937  * @param {String/Array} selector The CSS selector or an array of elements
9938  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9939  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9940  * @return {CompositeElementLite/CompositeElement}
9941  * @member Roo
9942  * @method select
9943  */
9944 Roo.select = Roo.Element.select;
9945
9946
9947
9948
9949
9950
9951
9952
9953
9954
9955
9956
9957
9958
9959 /*
9960  * Based on:
9961  * Ext JS Library 1.1.1
9962  * Copyright(c) 2006-2007, Ext JS, LLC.
9963  *
9964  * Originally Released Under LGPL - original licence link has changed is not relivant.
9965  *
9966  * Fork - LGPL
9967  * <script type="text/javascript">
9968  */
9969
9970
9971
9972 //Notifies Element that fx methods are available
9973 Roo.enableFx = true;
9974
9975 /**
9976  * @class Roo.Fx
9977  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9978  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9979  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9980  * Element effects to work.</p><br/>
9981  *
9982  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9983  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9984  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9985  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9986  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9987  * expected results and should be done with care.</p><br/>
9988  *
9989  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9990  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9991 <pre>
9992 Value  Description
9993 -----  -----------------------------
9994 tl     The top left corner
9995 t      The center of the top edge
9996 tr     The top right corner
9997 l      The center of the left edge
9998 r      The center of the right edge
9999 bl     The bottom left corner
10000 b      The center of the bottom edge
10001 br     The bottom right corner
10002 </pre>
10003  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10004  * below are common options that can be passed to any Fx method.</b>
10005  * @cfg {Function} callback A function called when the effect is finished
10006  * @cfg {Object} scope The scope of the effect function
10007  * @cfg {String} easing A valid Easing value for the effect
10008  * @cfg {String} afterCls A css class to apply after the effect
10009  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10010  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10011  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10012  * effects that end with the element being visually hidden, ignored otherwise)
10013  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10014  * a function which returns such a specification that will be applied to the Element after the effect finishes
10015  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10016  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
10017  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10018  */
10019 Roo.Fx = {
10020         /**
10021          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10022          * origin for the slide effect.  This function automatically handles wrapping the element with
10023          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10024          * Usage:
10025          *<pre><code>
10026 // default: slide the element in from the top
10027 el.slideIn();
10028
10029 // custom: slide the element in from the right with a 2-second duration
10030 el.slideIn('r', { duration: 2 });
10031
10032 // common config options shown with default values
10033 el.slideIn('t', {
10034     easing: 'easeOut',
10035     duration: .5
10036 });
10037 </code></pre>
10038          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10039          * @param {Object} options (optional) Object literal with any of the Fx config options
10040          * @return {Roo.Element} The Element
10041          */
10042     slideIn : function(anchor, o){
10043         var el = this.getFxEl();
10044         o = o || {};
10045
10046         el.queueFx(o, function(){
10047
10048             anchor = anchor || "t";
10049
10050             // fix display to visibility
10051             this.fixDisplay();
10052
10053             // restore values after effect
10054             var r = this.getFxRestore();
10055             var b = this.getBox();
10056             // fixed size for slide
10057             this.setSize(b);
10058
10059             // wrap if needed
10060             var wrap = this.fxWrap(r.pos, o, "hidden");
10061
10062             var st = this.dom.style;
10063             st.visibility = "visible";
10064             st.position = "absolute";
10065
10066             // clear out temp styles after slide and unwrap
10067             var after = function(){
10068                 el.fxUnwrap(wrap, r.pos, o);
10069                 st.width = r.width;
10070                 st.height = r.height;
10071                 el.afterFx(o);
10072             };
10073             // time to calc the positions
10074             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10075
10076             switch(anchor.toLowerCase()){
10077                 case "t":
10078                     wrap.setSize(b.width, 0);
10079                     st.left = st.bottom = "0";
10080                     a = {height: bh};
10081                 break;
10082                 case "l":
10083                     wrap.setSize(0, b.height);
10084                     st.right = st.top = "0";
10085                     a = {width: bw};
10086                 break;
10087                 case "r":
10088                     wrap.setSize(0, b.height);
10089                     wrap.setX(b.right);
10090                     st.left = st.top = "0";
10091                     a = {width: bw, points: pt};
10092                 break;
10093                 case "b":
10094                     wrap.setSize(b.width, 0);
10095                     wrap.setY(b.bottom);
10096                     st.left = st.top = "0";
10097                     a = {height: bh, points: pt};
10098                 break;
10099                 case "tl":
10100                     wrap.setSize(0, 0);
10101                     st.right = st.bottom = "0";
10102                     a = {width: bw, height: bh};
10103                 break;
10104                 case "bl":
10105                     wrap.setSize(0, 0);
10106                     wrap.setY(b.y+b.height);
10107                     st.right = st.top = "0";
10108                     a = {width: bw, height: bh, points: pt};
10109                 break;
10110                 case "br":
10111                     wrap.setSize(0, 0);
10112                     wrap.setXY([b.right, b.bottom]);
10113                     st.left = st.top = "0";
10114                     a = {width: bw, height: bh, points: pt};
10115                 break;
10116                 case "tr":
10117                     wrap.setSize(0, 0);
10118                     wrap.setX(b.x+b.width);
10119                     st.left = st.bottom = "0";
10120                     a = {width: bw, height: bh, points: pt};
10121                 break;
10122             }
10123             this.dom.style.visibility = "visible";
10124             wrap.show();
10125
10126             arguments.callee.anim = wrap.fxanim(a,
10127                 o,
10128                 'motion',
10129                 .5,
10130                 'easeOut', after);
10131         });
10132         return this;
10133     },
10134     
10135         /**
10136          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10137          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10138          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10139          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10140          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10141          * Usage:
10142          *<pre><code>
10143 // default: slide the element out to the top
10144 el.slideOut();
10145
10146 // custom: slide the element out to the right with a 2-second duration
10147 el.slideOut('r', { duration: 2 });
10148
10149 // common config options shown with default values
10150 el.slideOut('t', {
10151     easing: 'easeOut',
10152     duration: .5,
10153     remove: false,
10154     useDisplay: false
10155 });
10156 </code></pre>
10157          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10158          * @param {Object} options (optional) Object literal with any of the Fx config options
10159          * @return {Roo.Element} The Element
10160          */
10161     slideOut : function(anchor, o){
10162         var el = this.getFxEl();
10163         o = o || {};
10164
10165         el.queueFx(o, function(){
10166
10167             anchor = anchor || "t";
10168
10169             // restore values after effect
10170             var r = this.getFxRestore();
10171             
10172             var b = this.getBox();
10173             // fixed size for slide
10174             this.setSize(b);
10175
10176             // wrap if needed
10177             var wrap = this.fxWrap(r.pos, o, "visible");
10178
10179             var st = this.dom.style;
10180             st.visibility = "visible";
10181             st.position = "absolute";
10182
10183             wrap.setSize(b);
10184
10185             var after = function(){
10186                 if(o.useDisplay){
10187                     el.setDisplayed(false);
10188                 }else{
10189                     el.hide();
10190                 }
10191
10192                 el.fxUnwrap(wrap, r.pos, o);
10193
10194                 st.width = r.width;
10195                 st.height = r.height;
10196
10197                 el.afterFx(o);
10198             };
10199
10200             var a, zero = {to: 0};
10201             switch(anchor.toLowerCase()){
10202                 case "t":
10203                     st.left = st.bottom = "0";
10204                     a = {height: zero};
10205                 break;
10206                 case "l":
10207                     st.right = st.top = "0";
10208                     a = {width: zero};
10209                 break;
10210                 case "r":
10211                     st.left = st.top = "0";
10212                     a = {width: zero, points: {to:[b.right, b.y]}};
10213                 break;
10214                 case "b":
10215                     st.left = st.top = "0";
10216                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10217                 break;
10218                 case "tl":
10219                     st.right = st.bottom = "0";
10220                     a = {width: zero, height: zero};
10221                 break;
10222                 case "bl":
10223                     st.right = st.top = "0";
10224                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10225                 break;
10226                 case "br":
10227                     st.left = st.top = "0";
10228                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10229                 break;
10230                 case "tr":
10231                     st.left = st.bottom = "0";
10232                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10233                 break;
10234             }
10235
10236             arguments.callee.anim = wrap.fxanim(a,
10237                 o,
10238                 'motion',
10239                 .5,
10240                 "easeOut", after);
10241         });
10242         return this;
10243     },
10244
10245         /**
10246          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10247          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10248          * The element must be removed from the DOM using the 'remove' config option if desired.
10249          * Usage:
10250          *<pre><code>
10251 // default
10252 el.puff();
10253
10254 // common config options shown with default values
10255 el.puff({
10256     easing: 'easeOut',
10257     duration: .5,
10258     remove: false,
10259     useDisplay: false
10260 });
10261 </code></pre>
10262          * @param {Object} options (optional) Object literal with any of the Fx config options
10263          * @return {Roo.Element} The Element
10264          */
10265     puff : function(o){
10266         var el = this.getFxEl();
10267         o = o || {};
10268
10269         el.queueFx(o, function(){
10270             this.clearOpacity();
10271             this.show();
10272
10273             // restore values after effect
10274             var r = this.getFxRestore();
10275             var st = this.dom.style;
10276
10277             var after = function(){
10278                 if(o.useDisplay){
10279                     el.setDisplayed(false);
10280                 }else{
10281                     el.hide();
10282                 }
10283
10284                 el.clearOpacity();
10285
10286                 el.setPositioning(r.pos);
10287                 st.width = r.width;
10288                 st.height = r.height;
10289                 st.fontSize = '';
10290                 el.afterFx(o);
10291             };
10292
10293             var width = this.getWidth();
10294             var height = this.getHeight();
10295
10296             arguments.callee.anim = this.fxanim({
10297                     width : {to: this.adjustWidth(width * 2)},
10298                     height : {to: this.adjustHeight(height * 2)},
10299                     points : {by: [-(width * .5), -(height * .5)]},
10300                     opacity : {to: 0},
10301                     fontSize: {to:200, unit: "%"}
10302                 },
10303                 o,
10304                 'motion',
10305                 .5,
10306                 "easeOut", after);
10307         });
10308         return this;
10309     },
10310
10311         /**
10312          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10313          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10314          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10315          * Usage:
10316          *<pre><code>
10317 // default
10318 el.switchOff();
10319
10320 // all config options shown with default values
10321 el.switchOff({
10322     easing: 'easeIn',
10323     duration: .3,
10324     remove: false,
10325     useDisplay: false
10326 });
10327 </code></pre>
10328          * @param {Object} options (optional) Object literal with any of the Fx config options
10329          * @return {Roo.Element} The Element
10330          */
10331     switchOff : function(o){
10332         var el = this.getFxEl();
10333         o = o || {};
10334
10335         el.queueFx(o, function(){
10336             this.clearOpacity();
10337             this.clip();
10338
10339             // restore values after effect
10340             var r = this.getFxRestore();
10341             var st = this.dom.style;
10342
10343             var after = function(){
10344                 if(o.useDisplay){
10345                     el.setDisplayed(false);
10346                 }else{
10347                     el.hide();
10348                 }
10349
10350                 el.clearOpacity();
10351                 el.setPositioning(r.pos);
10352                 st.width = r.width;
10353                 st.height = r.height;
10354
10355                 el.afterFx(o);
10356             };
10357
10358             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10359                 this.clearOpacity();
10360                 (function(){
10361                     this.fxanim({
10362                         height:{to:1},
10363                         points:{by:[0, this.getHeight() * .5]}
10364                     }, o, 'motion', 0.3, 'easeIn', after);
10365                 }).defer(100, this);
10366             });
10367         });
10368         return this;
10369     },
10370
10371     /**
10372      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10373      * changed using the "attr" config option) and then fading back to the original color. If no original
10374      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10375      * Usage:
10376 <pre><code>
10377 // default: highlight background to yellow
10378 el.highlight();
10379
10380 // custom: highlight foreground text to blue for 2 seconds
10381 el.highlight("0000ff", { attr: 'color', duration: 2 });
10382
10383 // common config options shown with default values
10384 el.highlight("ffff9c", {
10385     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10386     endColor: (current color) or "ffffff",
10387     easing: 'easeIn',
10388     duration: 1
10389 });
10390 </code></pre>
10391      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10392      * @param {Object} options (optional) Object literal with any of the Fx config options
10393      * @return {Roo.Element} The Element
10394      */ 
10395     highlight : function(color, o){
10396         var el = this.getFxEl();
10397         o = o || {};
10398
10399         el.queueFx(o, function(){
10400             color = color || "ffff9c";
10401             attr = o.attr || "backgroundColor";
10402
10403             this.clearOpacity();
10404             this.show();
10405
10406             var origColor = this.getColor(attr);
10407             var restoreColor = this.dom.style[attr];
10408             endColor = (o.endColor || origColor) || "ffffff";
10409
10410             var after = function(){
10411                 el.dom.style[attr] = restoreColor;
10412                 el.afterFx(o);
10413             };
10414
10415             var a = {};
10416             a[attr] = {from: color, to: endColor};
10417             arguments.callee.anim = this.fxanim(a,
10418                 o,
10419                 'color',
10420                 1,
10421                 'easeIn', after);
10422         });
10423         return this;
10424     },
10425
10426    /**
10427     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10428     * Usage:
10429 <pre><code>
10430 // default: a single light blue ripple
10431 el.frame();
10432
10433 // custom: 3 red ripples lasting 3 seconds total
10434 el.frame("ff0000", 3, { duration: 3 });
10435
10436 // common config options shown with default values
10437 el.frame("C3DAF9", 1, {
10438     duration: 1 //duration of entire animation (not each individual ripple)
10439     // Note: Easing is not configurable and will be ignored if included
10440 });
10441 </code></pre>
10442     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10443     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10444     * @param {Object} options (optional) Object literal with any of the Fx config options
10445     * @return {Roo.Element} The Element
10446     */
10447     frame : function(color, count, o){
10448         var el = this.getFxEl();
10449         o = o || {};
10450
10451         el.queueFx(o, function(){
10452             color = color || "#C3DAF9";
10453             if(color.length == 6){
10454                 color = "#" + color;
10455             }
10456             count = count || 1;
10457             duration = o.duration || 1;
10458             this.show();
10459
10460             var b = this.getBox();
10461             var animFn = function(){
10462                 var proxy = this.createProxy({
10463
10464                      style:{
10465                         visbility:"hidden",
10466                         position:"absolute",
10467                         "z-index":"35000", // yee haw
10468                         border:"0px solid " + color
10469                      }
10470                   });
10471                 var scale = Roo.isBorderBox ? 2 : 1;
10472                 proxy.animate({
10473                     top:{from:b.y, to:b.y - 20},
10474                     left:{from:b.x, to:b.x - 20},
10475                     borderWidth:{from:0, to:10},
10476                     opacity:{from:1, to:0},
10477                     height:{from:b.height, to:(b.height + (20*scale))},
10478                     width:{from:b.width, to:(b.width + (20*scale))}
10479                 }, duration, function(){
10480                     proxy.remove();
10481                 });
10482                 if(--count > 0){
10483                      animFn.defer((duration/2)*1000, this);
10484                 }else{
10485                     el.afterFx(o);
10486                 }
10487             };
10488             animFn.call(this);
10489         });
10490         return this;
10491     },
10492
10493    /**
10494     * Creates a pause before any subsequent queued effects begin.  If there are
10495     * no effects queued after the pause it will have no effect.
10496     * Usage:
10497 <pre><code>
10498 el.pause(1);
10499 </code></pre>
10500     * @param {Number} seconds The length of time to pause (in seconds)
10501     * @return {Roo.Element} The Element
10502     */
10503     pause : function(seconds){
10504         var el = this.getFxEl();
10505         var o = {};
10506
10507         el.queueFx(o, function(){
10508             setTimeout(function(){
10509                 el.afterFx(o);
10510             }, seconds * 1000);
10511         });
10512         return this;
10513     },
10514
10515    /**
10516     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10517     * using the "endOpacity" config option.
10518     * Usage:
10519 <pre><code>
10520 // default: fade in from opacity 0 to 100%
10521 el.fadeIn();
10522
10523 // custom: fade in from opacity 0 to 75% over 2 seconds
10524 el.fadeIn({ endOpacity: .75, duration: 2});
10525
10526 // common config options shown with default values
10527 el.fadeIn({
10528     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10529     easing: 'easeOut',
10530     duration: .5
10531 });
10532 </code></pre>
10533     * @param {Object} options (optional) Object literal with any of the Fx config options
10534     * @return {Roo.Element} The Element
10535     */
10536     fadeIn : function(o){
10537         var el = this.getFxEl();
10538         o = o || {};
10539         el.queueFx(o, function(){
10540             this.setOpacity(0);
10541             this.fixDisplay();
10542             this.dom.style.visibility = 'visible';
10543             var to = o.endOpacity || 1;
10544             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10545                 o, null, .5, "easeOut", function(){
10546                 if(to == 1){
10547                     this.clearOpacity();
10548                 }
10549                 el.afterFx(o);
10550             });
10551         });
10552         return this;
10553     },
10554
10555    /**
10556     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10557     * using the "endOpacity" config option.
10558     * Usage:
10559 <pre><code>
10560 // default: fade out from the element's current opacity to 0
10561 el.fadeOut();
10562
10563 // custom: fade out from the element's current opacity to 25% over 2 seconds
10564 el.fadeOut({ endOpacity: .25, duration: 2});
10565
10566 // common config options shown with default values
10567 el.fadeOut({
10568     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10569     easing: 'easeOut',
10570     duration: .5
10571     remove: false,
10572     useDisplay: false
10573 });
10574 </code></pre>
10575     * @param {Object} options (optional) Object literal with any of the Fx config options
10576     * @return {Roo.Element} The Element
10577     */
10578     fadeOut : function(o){
10579         var el = this.getFxEl();
10580         o = o || {};
10581         el.queueFx(o, function(){
10582             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10583                 o, null, .5, "easeOut", function(){
10584                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10585                      this.dom.style.display = "none";
10586                 }else{
10587                      this.dom.style.visibility = "hidden";
10588                 }
10589                 this.clearOpacity();
10590                 el.afterFx(o);
10591             });
10592         });
10593         return this;
10594     },
10595
10596    /**
10597     * Animates the transition of an element's dimensions from a starting height/width
10598     * to an ending height/width.
10599     * Usage:
10600 <pre><code>
10601 // change height and width to 100x100 pixels
10602 el.scale(100, 100);
10603
10604 // common config options shown with default values.  The height and width will default to
10605 // the element's existing values if passed as null.
10606 el.scale(
10607     [element's width],
10608     [element's height], {
10609     easing: 'easeOut',
10610     duration: .35
10611 });
10612 </code></pre>
10613     * @param {Number} width  The new width (pass undefined to keep the original width)
10614     * @param {Number} height  The new height (pass undefined to keep the original height)
10615     * @param {Object} options (optional) Object literal with any of the Fx config options
10616     * @return {Roo.Element} The Element
10617     */
10618     scale : function(w, h, o){
10619         this.shift(Roo.apply({}, o, {
10620             width: w,
10621             height: h
10622         }));
10623         return this;
10624     },
10625
10626    /**
10627     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10628     * Any of these properties not specified in the config object will not be changed.  This effect 
10629     * requires that at least one new dimension, position or opacity setting must be passed in on
10630     * the config object in order for the function to have any effect.
10631     * Usage:
10632 <pre><code>
10633 // slide the element horizontally to x position 200 while changing the height and opacity
10634 el.shift({ x: 200, height: 50, opacity: .8 });
10635
10636 // common config options shown with default values.
10637 el.shift({
10638     width: [element's width],
10639     height: [element's height],
10640     x: [element's x position],
10641     y: [element's y position],
10642     opacity: [element's opacity],
10643     easing: 'easeOut',
10644     duration: .35
10645 });
10646 </code></pre>
10647     * @param {Object} options  Object literal with any of the Fx config options
10648     * @return {Roo.Element} The Element
10649     */
10650     shift : function(o){
10651         var el = this.getFxEl();
10652         o = o || {};
10653         el.queueFx(o, function(){
10654             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10655             if(w !== undefined){
10656                 a.width = {to: this.adjustWidth(w)};
10657             }
10658             if(h !== undefined){
10659                 a.height = {to: this.adjustHeight(h)};
10660             }
10661             if(x !== undefined || y !== undefined){
10662                 a.points = {to: [
10663                     x !== undefined ? x : this.getX(),
10664                     y !== undefined ? y : this.getY()
10665                 ]};
10666             }
10667             if(op !== undefined){
10668                 a.opacity = {to: op};
10669             }
10670             if(o.xy !== undefined){
10671                 a.points = {to: o.xy};
10672             }
10673             arguments.callee.anim = this.fxanim(a,
10674                 o, 'motion', .35, "easeOut", function(){
10675                 el.afterFx(o);
10676             });
10677         });
10678         return this;
10679     },
10680
10681         /**
10682          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10683          * ending point of the effect.
10684          * Usage:
10685          *<pre><code>
10686 // default: slide the element downward while fading out
10687 el.ghost();
10688
10689 // custom: slide the element out to the right with a 2-second duration
10690 el.ghost('r', { duration: 2 });
10691
10692 // common config options shown with default values
10693 el.ghost('b', {
10694     easing: 'easeOut',
10695     duration: .5
10696     remove: false,
10697     useDisplay: false
10698 });
10699 </code></pre>
10700          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10701          * @param {Object} options (optional) Object literal with any of the Fx config options
10702          * @return {Roo.Element} The Element
10703          */
10704     ghost : function(anchor, o){
10705         var el = this.getFxEl();
10706         o = o || {};
10707
10708         el.queueFx(o, function(){
10709             anchor = anchor || "b";
10710
10711             // restore values after effect
10712             var r = this.getFxRestore();
10713             var w = this.getWidth(),
10714                 h = this.getHeight();
10715
10716             var st = this.dom.style;
10717
10718             var after = function(){
10719                 if(o.useDisplay){
10720                     el.setDisplayed(false);
10721                 }else{
10722                     el.hide();
10723                 }
10724
10725                 el.clearOpacity();
10726                 el.setPositioning(r.pos);
10727                 st.width = r.width;
10728                 st.height = r.height;
10729
10730                 el.afterFx(o);
10731             };
10732
10733             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10734             switch(anchor.toLowerCase()){
10735                 case "t":
10736                     pt.by = [0, -h];
10737                 break;
10738                 case "l":
10739                     pt.by = [-w, 0];
10740                 break;
10741                 case "r":
10742                     pt.by = [w, 0];
10743                 break;
10744                 case "b":
10745                     pt.by = [0, h];
10746                 break;
10747                 case "tl":
10748                     pt.by = [-w, -h];
10749                 break;
10750                 case "bl":
10751                     pt.by = [-w, h];
10752                 break;
10753                 case "br":
10754                     pt.by = [w, h];
10755                 break;
10756                 case "tr":
10757                     pt.by = [w, -h];
10758                 break;
10759             }
10760
10761             arguments.callee.anim = this.fxanim(a,
10762                 o,
10763                 'motion',
10764                 .5,
10765                 "easeOut", after);
10766         });
10767         return this;
10768     },
10769
10770         /**
10771          * Ensures that all effects queued after syncFx is called on the element are
10772          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10773          * @return {Roo.Element} The Element
10774          */
10775     syncFx : function(){
10776         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10777             block : false,
10778             concurrent : true,
10779             stopFx : false
10780         });
10781         return this;
10782     },
10783
10784         /**
10785          * Ensures that all effects queued after sequenceFx is called on the element are
10786          * run in sequence.  This is the opposite of {@link #syncFx}.
10787          * @return {Roo.Element} The Element
10788          */
10789     sequenceFx : function(){
10790         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10791             block : false,
10792             concurrent : false,
10793             stopFx : false
10794         });
10795         return this;
10796     },
10797
10798         /* @private */
10799     nextFx : function(){
10800         var ef = this.fxQueue[0];
10801         if(ef){
10802             ef.call(this);
10803         }
10804     },
10805
10806         /**
10807          * Returns true if the element has any effects actively running or queued, else returns false.
10808          * @return {Boolean} True if element has active effects, else false
10809          */
10810     hasActiveFx : function(){
10811         return this.fxQueue && this.fxQueue[0];
10812     },
10813
10814         /**
10815          * Stops any running effects and clears the element's internal effects queue if it contains
10816          * any additional effects that haven't started yet.
10817          * @return {Roo.Element} The Element
10818          */
10819     stopFx : function(){
10820         if(this.hasActiveFx()){
10821             var cur = this.fxQueue[0];
10822             if(cur && cur.anim && cur.anim.isAnimated()){
10823                 this.fxQueue = [cur]; // clear out others
10824                 cur.anim.stop(true);
10825             }
10826         }
10827         return this;
10828     },
10829
10830         /* @private */
10831     beforeFx : function(o){
10832         if(this.hasActiveFx() && !o.concurrent){
10833            if(o.stopFx){
10834                this.stopFx();
10835                return true;
10836            }
10837            return false;
10838         }
10839         return true;
10840     },
10841
10842         /**
10843          * Returns true if the element is currently blocking so that no other effect can be queued
10844          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10845          * used to ensure that an effect initiated by a user action runs to completion prior to the
10846          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10847          * @return {Boolean} True if blocking, else false
10848          */
10849     hasFxBlock : function(){
10850         var q = this.fxQueue;
10851         return q && q[0] && q[0].block;
10852     },
10853
10854         /* @private */
10855     queueFx : function(o, fn){
10856         if(!this.fxQueue){
10857             this.fxQueue = [];
10858         }
10859         if(!this.hasFxBlock()){
10860             Roo.applyIf(o, this.fxDefaults);
10861             if(!o.concurrent){
10862                 var run = this.beforeFx(o);
10863                 fn.block = o.block;
10864                 this.fxQueue.push(fn);
10865                 if(run){
10866                     this.nextFx();
10867                 }
10868             }else{
10869                 fn.call(this);
10870             }
10871         }
10872         return this;
10873     },
10874
10875         /* @private */
10876     fxWrap : function(pos, o, vis){
10877         var wrap;
10878         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10879             var wrapXY;
10880             if(o.fixPosition){
10881                 wrapXY = this.getXY();
10882             }
10883             var div = document.createElement("div");
10884             div.style.visibility = vis;
10885             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10886             wrap.setPositioning(pos);
10887             if(wrap.getStyle("position") == "static"){
10888                 wrap.position("relative");
10889             }
10890             this.clearPositioning('auto');
10891             wrap.clip();
10892             wrap.dom.appendChild(this.dom);
10893             if(wrapXY){
10894                 wrap.setXY(wrapXY);
10895             }
10896         }
10897         return wrap;
10898     },
10899
10900         /* @private */
10901     fxUnwrap : function(wrap, pos, o){
10902         this.clearPositioning();
10903         this.setPositioning(pos);
10904         if(!o.wrap){
10905             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10906             wrap.remove();
10907         }
10908     },
10909
10910         /* @private */
10911     getFxRestore : function(){
10912         var st = this.dom.style;
10913         return {pos: this.getPositioning(), width: st.width, height : st.height};
10914     },
10915
10916         /* @private */
10917     afterFx : function(o){
10918         if(o.afterStyle){
10919             this.applyStyles(o.afterStyle);
10920         }
10921         if(o.afterCls){
10922             this.addClass(o.afterCls);
10923         }
10924         if(o.remove === true){
10925             this.remove();
10926         }
10927         Roo.callback(o.callback, o.scope, [this]);
10928         if(!o.concurrent){
10929             this.fxQueue.shift();
10930             this.nextFx();
10931         }
10932     },
10933
10934         /* @private */
10935     getFxEl : function(){ // support for composite element fx
10936         return Roo.get(this.dom);
10937     },
10938
10939         /* @private */
10940     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10941         animType = animType || 'run';
10942         opt = opt || {};
10943         var anim = Roo.lib.Anim[animType](
10944             this.dom, args,
10945             (opt.duration || defaultDur) || .35,
10946             (opt.easing || defaultEase) || 'easeOut',
10947             function(){
10948                 Roo.callback(cb, this);
10949             },
10950             this
10951         );
10952         opt.anim = anim;
10953         return anim;
10954     }
10955 };
10956
10957 // backwords compat
10958 Roo.Fx.resize = Roo.Fx.scale;
10959
10960 //When included, Roo.Fx is automatically applied to Element so that all basic
10961 //effects are available directly via the Element API
10962 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10963  * Based on:
10964  * Ext JS Library 1.1.1
10965  * Copyright(c) 2006-2007, Ext JS, LLC.
10966  *
10967  * Originally Released Under LGPL - original licence link has changed is not relivant.
10968  *
10969  * Fork - LGPL
10970  * <script type="text/javascript">
10971  */
10972
10973
10974 /**
10975  * @class Roo.CompositeElement
10976  * Standard composite class. Creates a Roo.Element for every element in the collection.
10977  * <br><br>
10978  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10979  * actions will be performed on all the elements in this collection.</b>
10980  * <br><br>
10981  * All methods return <i>this</i> and can be chained.
10982  <pre><code>
10983  var els = Roo.select("#some-el div.some-class", true);
10984  // or select directly from an existing element
10985  var el = Roo.get('some-el');
10986  el.select('div.some-class', true);
10987
10988  els.setWidth(100); // all elements become 100 width
10989  els.hide(true); // all elements fade out and hide
10990  // or
10991  els.setWidth(100).hide(true);
10992  </code></pre>
10993  */
10994 Roo.CompositeElement = function(els){
10995     this.elements = [];
10996     this.addElements(els);
10997 };
10998 Roo.CompositeElement.prototype = {
10999     isComposite: true,
11000     addElements : function(els){
11001         if(!els) return this;
11002         if(typeof els == "string"){
11003             els = Roo.Element.selectorFunction(els);
11004         }
11005         var yels = this.elements;
11006         var index = yels.length-1;
11007         for(var i = 0, len = els.length; i < len; i++) {
11008                 yels[++index] = Roo.get(els[i]);
11009         }
11010         return this;
11011     },
11012
11013     /**
11014     * Clears this composite and adds the elements returned by the passed selector.
11015     * @param {String/Array} els A string CSS selector, an array of elements or an element
11016     * @return {CompositeElement} this
11017     */
11018     fill : function(els){
11019         this.elements = [];
11020         this.add(els);
11021         return this;
11022     },
11023
11024     /**
11025     * Filters this composite to only elements that match the passed selector.
11026     * @param {String} selector A string CSS selector
11027     * @param {Boolean} inverse return inverse filter (not matches)
11028     * @return {CompositeElement} this
11029     */
11030     filter : function(selector, inverse){
11031         var els = [];
11032         inverse = inverse || false;
11033         this.each(function(el){
11034             var match = inverse ? !el.is(selector) : el.is(selector);
11035             if(match){
11036                 els[els.length] = el.dom;
11037             }
11038         });
11039         this.fill(els);
11040         return this;
11041     },
11042
11043     invoke : function(fn, args){
11044         var els = this.elements;
11045         for(var i = 0, len = els.length; i < len; i++) {
11046                 Roo.Element.prototype[fn].apply(els[i], args);
11047         }
11048         return this;
11049     },
11050     /**
11051     * Adds elements to this composite.
11052     * @param {String/Array} els A string CSS selector, an array of elements or an element
11053     * @return {CompositeElement} this
11054     */
11055     add : function(els){
11056         if(typeof els == "string"){
11057             this.addElements(Roo.Element.selectorFunction(els));
11058         }else if(els.length !== undefined){
11059             this.addElements(els);
11060         }else{
11061             this.addElements([els]);
11062         }
11063         return this;
11064     },
11065     /**
11066     * Calls the passed function passing (el, this, index) for each element in this composite.
11067     * @param {Function} fn The function to call
11068     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11069     * @return {CompositeElement} this
11070     */
11071     each : function(fn, scope){
11072         var els = this.elements;
11073         for(var i = 0, len = els.length; i < len; i++){
11074             if(fn.call(scope || els[i], els[i], this, i) === false) {
11075                 break;
11076             }
11077         }
11078         return this;
11079     },
11080
11081     /**
11082      * Returns the Element object at the specified index
11083      * @param {Number} index
11084      * @return {Roo.Element}
11085      */
11086     item : function(index){
11087         return this.elements[index] || null;
11088     },
11089
11090     /**
11091      * Returns the first Element
11092      * @return {Roo.Element}
11093      */
11094     first : function(){
11095         return this.item(0);
11096     },
11097
11098     /**
11099      * Returns the last Element
11100      * @return {Roo.Element}
11101      */
11102     last : function(){
11103         return this.item(this.elements.length-1);
11104     },
11105
11106     /**
11107      * Returns the number of elements in this composite
11108      * @return Number
11109      */
11110     getCount : function(){
11111         return this.elements.length;
11112     },
11113
11114     /**
11115      * Returns true if this composite contains the passed element
11116      * @return Boolean
11117      */
11118     contains : function(el){
11119         return this.indexOf(el) !== -1;
11120     },
11121
11122     /**
11123      * Returns true if this composite contains the passed element
11124      * @return Boolean
11125      */
11126     indexOf : function(el){
11127         return this.elements.indexOf(Roo.get(el));
11128     },
11129
11130
11131     /**
11132     * Removes the specified element(s).
11133     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11134     * or an array of any of those.
11135     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11136     * @return {CompositeElement} this
11137     */
11138     removeElement : function(el, removeDom){
11139         if(el instanceof Array){
11140             for(var i = 0, len = el.length; i < len; i++){
11141                 this.removeElement(el[i]);
11142             }
11143             return this;
11144         }
11145         var index = typeof el == 'number' ? el : this.indexOf(el);
11146         if(index !== -1){
11147             if(removeDom){
11148                 var d = this.elements[index];
11149                 if(d.dom){
11150                     d.remove();
11151                 }else{
11152                     d.parentNode.removeChild(d);
11153                 }
11154             }
11155             this.elements.splice(index, 1);
11156         }
11157         return this;
11158     },
11159
11160     /**
11161     * Replaces the specified element with the passed element.
11162     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11163     * to replace.
11164     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11165     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11166     * @return {CompositeElement} this
11167     */
11168     replaceElement : function(el, replacement, domReplace){
11169         var index = typeof el == 'number' ? el : this.indexOf(el);
11170         if(index !== -1){
11171             if(domReplace){
11172                 this.elements[index].replaceWith(replacement);
11173             }else{
11174                 this.elements.splice(index, 1, Roo.get(replacement))
11175             }
11176         }
11177         return this;
11178     },
11179
11180     /**
11181      * Removes all elements.
11182      */
11183     clear : function(){
11184         this.elements = [];
11185     }
11186 };
11187 (function(){
11188     Roo.CompositeElement.createCall = function(proto, fnName){
11189         if(!proto[fnName]){
11190             proto[fnName] = function(){
11191                 return this.invoke(fnName, arguments);
11192             };
11193         }
11194     };
11195     for(var fnName in Roo.Element.prototype){
11196         if(typeof Roo.Element.prototype[fnName] == "function"){
11197             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11198         }
11199     };
11200 })();
11201 /*
11202  * Based on:
11203  * Ext JS Library 1.1.1
11204  * Copyright(c) 2006-2007, Ext JS, LLC.
11205  *
11206  * Originally Released Under LGPL - original licence link has changed is not relivant.
11207  *
11208  * Fork - LGPL
11209  * <script type="text/javascript">
11210  */
11211
11212 /**
11213  * @class Roo.CompositeElementLite
11214  * @extends Roo.CompositeElement
11215  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11216  <pre><code>
11217  var els = Roo.select("#some-el div.some-class");
11218  // or select directly from an existing element
11219  var el = Roo.get('some-el');
11220  el.select('div.some-class');
11221
11222  els.setWidth(100); // all elements become 100 width
11223  els.hide(true); // all elements fade out and hide
11224  // or
11225  els.setWidth(100).hide(true);
11226  </code></pre><br><br>
11227  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11228  * actions will be performed on all the elements in this collection.</b>
11229  */
11230 Roo.CompositeElementLite = function(els){
11231     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11232     this.el = new Roo.Element.Flyweight();
11233 };
11234 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11235     addElements : function(els){
11236         if(els){
11237             if(els instanceof Array){
11238                 this.elements = this.elements.concat(els);
11239             }else{
11240                 var yels = this.elements;
11241                 var index = yels.length-1;
11242                 for(var i = 0, len = els.length; i < len; i++) {
11243                     yels[++index] = els[i];
11244                 }
11245             }
11246         }
11247         return this;
11248     },
11249     invoke : function(fn, args){
11250         var els = this.elements;
11251         var el = this.el;
11252         for(var i = 0, len = els.length; i < len; i++) {
11253             el.dom = els[i];
11254                 Roo.Element.prototype[fn].apply(el, args);
11255         }
11256         return this;
11257     },
11258     /**
11259      * Returns a flyweight Element of the dom element object at the specified index
11260      * @param {Number} index
11261      * @return {Roo.Element}
11262      */
11263     item : function(index){
11264         if(!this.elements[index]){
11265             return null;
11266         }
11267         this.el.dom = this.elements[index];
11268         return this.el;
11269     },
11270
11271     // fixes scope with flyweight
11272     addListener : function(eventName, handler, scope, opt){
11273         var els = this.elements;
11274         for(var i = 0, len = els.length; i < len; i++) {
11275             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11276         }
11277         return this;
11278     },
11279
11280     /**
11281     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11282     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11283     * a reference to the dom node, use el.dom.</b>
11284     * @param {Function} fn The function to call
11285     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11286     * @return {CompositeElement} this
11287     */
11288     each : function(fn, scope){
11289         var els = this.elements;
11290         var el = this.el;
11291         for(var i = 0, len = els.length; i < len; i++){
11292             el.dom = els[i];
11293                 if(fn.call(scope || el, el, this, i) === false){
11294                 break;
11295             }
11296         }
11297         return this;
11298     },
11299
11300     indexOf : function(el){
11301         return this.elements.indexOf(Roo.getDom(el));
11302     },
11303
11304     replaceElement : function(el, replacement, domReplace){
11305         var index = typeof el == 'number' ? el : this.indexOf(el);
11306         if(index !== -1){
11307             replacement = Roo.getDom(replacement);
11308             if(domReplace){
11309                 var d = this.elements[index];
11310                 d.parentNode.insertBefore(replacement, d);
11311                 d.parentNode.removeChild(d);
11312             }
11313             this.elements.splice(index, 1, replacement);
11314         }
11315         return this;
11316     }
11317 });
11318 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11319
11320 /*
11321  * Based on:
11322  * Ext JS Library 1.1.1
11323  * Copyright(c) 2006-2007, Ext JS, LLC.
11324  *
11325  * Originally Released Under LGPL - original licence link has changed is not relivant.
11326  *
11327  * Fork - LGPL
11328  * <script type="text/javascript">
11329  */
11330
11331  
11332
11333 /**
11334  * @class Roo.data.Connection
11335  * @extends Roo.util.Observable
11336  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11337  * either to a configured URL, or to a URL specified at request time.<br><br>
11338  * <p>
11339  * Requests made by this class are asynchronous, and will return immediately. No data from
11340  * the server will be available to the statement immediately following the {@link #request} call.
11341  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11342  * <p>
11343  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11344  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11345  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11346  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11347  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11348  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11349  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11350  * standard DOM methods.
11351  * @constructor
11352  * @param {Object} config a configuration object.
11353  */
11354 Roo.data.Connection = function(config){
11355     Roo.apply(this, config);
11356     this.addEvents({
11357         /**
11358          * @event beforerequest
11359          * Fires before a network request is made to retrieve a data object.
11360          * @param {Connection} conn This Connection object.
11361          * @param {Object} options The options config object passed to the {@link #request} method.
11362          */
11363         "beforerequest" : true,
11364         /**
11365          * @event requestcomplete
11366          * Fires if the request was successfully completed.
11367          * @param {Connection} conn This Connection object.
11368          * @param {Object} response The XHR object containing the response data.
11369          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11370          * @param {Object} options The options config object passed to the {@link #request} method.
11371          */
11372         "requestcomplete" : true,
11373         /**
11374          * @event requestexception
11375          * Fires if an error HTTP status was returned from the server.
11376          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11377          * @param {Connection} conn This Connection object.
11378          * @param {Object} response The XHR object containing the response data.
11379          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11380          * @param {Object} options The options config object passed to the {@link #request} method.
11381          */
11382         "requestexception" : true
11383     });
11384     Roo.data.Connection.superclass.constructor.call(this);
11385 };
11386
11387 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11388     /**
11389      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11390      */
11391     /**
11392      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11393      * extra parameters to each request made by this object. (defaults to undefined)
11394      */
11395     /**
11396      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11397      *  to each request made by this object. (defaults to undefined)
11398      */
11399     /**
11400      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11401      */
11402     /**
11403      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11404      */
11405     timeout : 30000,
11406     /**
11407      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11408      * @type Boolean
11409      */
11410     autoAbort:false,
11411
11412     /**
11413      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11414      * @type Boolean
11415      */
11416     disableCaching: true,
11417
11418     /**
11419      * Sends an HTTP request to a remote server.
11420      * @param {Object} options An object which may contain the following properties:<ul>
11421      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11422      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11423      * request, a url encoded string or a function to call to get either.</li>
11424      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11425      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11426      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11427      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11428      * <li>options {Object} The parameter to the request call.</li>
11429      * <li>success {Boolean} True if the request succeeded.</li>
11430      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11431      * </ul></li>
11432      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11433      * The callback is passed the following parameters:<ul>
11434      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11435      * <li>options {Object} The parameter to the request call.</li>
11436      * </ul></li>
11437      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11438      * The callback is passed the following parameters:<ul>
11439      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11440      * <li>options {Object} The parameter to the request call.</li>
11441      * </ul></li>
11442      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11443      * for the callback function. Defaults to the browser window.</li>
11444      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11445      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11446      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11447      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11448      * params for the post data. Any params will be appended to the URL.</li>
11449      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11450      * </ul>
11451      * @return {Number} transactionId
11452      */
11453     request : function(o){
11454         if(this.fireEvent("beforerequest", this, o) !== false){
11455             var p = o.params;
11456
11457             if(typeof p == "function"){
11458                 p = p.call(o.scope||window, o);
11459             }
11460             if(typeof p == "object"){
11461                 p = Roo.urlEncode(o.params);
11462             }
11463             if(this.extraParams){
11464                 var extras = Roo.urlEncode(this.extraParams);
11465                 p = p ? (p + '&' + extras) : extras;
11466             }
11467
11468             var url = o.url || this.url;
11469             if(typeof url == 'function'){
11470                 url = url.call(o.scope||window, o);
11471             }
11472
11473             if(o.form){
11474                 var form = Roo.getDom(o.form);
11475                 url = url || form.action;
11476
11477                 var enctype = form.getAttribute("enctype");
11478                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11479                     return this.doFormUpload(o, p, url);
11480                 }
11481                 var f = Roo.lib.Ajax.serializeForm(form);
11482                 p = p ? (p + '&' + f) : f;
11483             }
11484
11485             var hs = o.headers;
11486             if(this.defaultHeaders){
11487                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11488                 if(!o.headers){
11489                     o.headers = hs;
11490                 }
11491             }
11492
11493             var cb = {
11494                 success: this.handleResponse,
11495                 failure: this.handleFailure,
11496                 scope: this,
11497                 argument: {options: o},
11498                 timeout : o.timeout || this.timeout
11499             };
11500
11501             var method = o.method||this.method||(p ? "POST" : "GET");
11502
11503             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11504                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11505             }
11506
11507             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11508                 if(o.autoAbort){
11509                     this.abort();
11510                 }
11511             }else if(this.autoAbort !== false){
11512                 this.abort();
11513             }
11514
11515             if((method == 'GET' && p) || o.xmlData){
11516                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11517                 p = '';
11518             }
11519             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11520             return this.transId;
11521         }else{
11522             Roo.callback(o.callback, o.scope, [o, null, null]);
11523             return null;
11524         }
11525     },
11526
11527     /**
11528      * Determine whether this object has a request outstanding.
11529      * @param {Number} transactionId (Optional) defaults to the last transaction
11530      * @return {Boolean} True if there is an outstanding request.
11531      */
11532     isLoading : function(transId){
11533         if(transId){
11534             return Roo.lib.Ajax.isCallInProgress(transId);
11535         }else{
11536             return this.transId ? true : false;
11537         }
11538     },
11539
11540     /**
11541      * Aborts any outstanding request.
11542      * @param {Number} transactionId (Optional) defaults to the last transaction
11543      */
11544     abort : function(transId){
11545         if(transId || this.isLoading()){
11546             Roo.lib.Ajax.abort(transId || this.transId);
11547         }
11548     },
11549
11550     // private
11551     handleResponse : function(response){
11552         this.transId = false;
11553         var options = response.argument.options;
11554         response.argument = options ? options.argument : null;
11555         this.fireEvent("requestcomplete", this, response, options);
11556         Roo.callback(options.success, options.scope, [response, options]);
11557         Roo.callback(options.callback, options.scope, [options, true, response]);
11558     },
11559
11560     // private
11561     handleFailure : function(response, e){
11562         this.transId = false;
11563         var options = response.argument.options;
11564         response.argument = options ? options.argument : null;
11565         this.fireEvent("requestexception", this, response, options, e);
11566         Roo.callback(options.failure, options.scope, [response, options]);
11567         Roo.callback(options.callback, options.scope, [options, false, response]);
11568     },
11569
11570     // private
11571     doFormUpload : function(o, ps, url){
11572         var id = Roo.id();
11573         var frame = document.createElement('iframe');
11574         frame.id = id;
11575         frame.name = id;
11576         frame.className = 'x-hidden';
11577         if(Roo.isIE){
11578             frame.src = Roo.SSL_SECURE_URL;
11579         }
11580         document.body.appendChild(frame);
11581
11582         if(Roo.isIE){
11583            document.frames[id].name = id;
11584         }
11585
11586         var form = Roo.getDom(o.form);
11587         form.target = id;
11588         form.method = 'POST';
11589         form.enctype = form.encoding = 'multipart/form-data';
11590         if(url){
11591             form.action = url;
11592         }
11593
11594         var hiddens, hd;
11595         if(ps){ // add dynamic params
11596             hiddens = [];
11597             ps = Roo.urlDecode(ps, false);
11598             for(var k in ps){
11599                 if(ps.hasOwnProperty(k)){
11600                     hd = document.createElement('input');
11601                     hd.type = 'hidden';
11602                     hd.name = k;
11603                     hd.value = ps[k];
11604                     form.appendChild(hd);
11605                     hiddens.push(hd);
11606                 }
11607             }
11608         }
11609
11610         function cb(){
11611             var r = {  // bogus response object
11612                 responseText : '',
11613                 responseXML : null
11614             };
11615
11616             r.argument = o ? o.argument : null;
11617
11618             try { //
11619                 var doc;
11620                 if(Roo.isIE){
11621                     doc = frame.contentWindow.document;
11622                 }else {
11623                     doc = (frame.contentDocument || window.frames[id].document);
11624                 }
11625                 if(doc && doc.body){
11626                     r.responseText = doc.body.innerHTML;
11627                 }
11628                 if(doc && doc.XMLDocument){
11629                     r.responseXML = doc.XMLDocument;
11630                 }else {
11631                     r.responseXML = doc;
11632                 }
11633             }
11634             catch(e) {
11635                 // ignore
11636             }
11637
11638             Roo.EventManager.removeListener(frame, 'load', cb, this);
11639
11640             this.fireEvent("requestcomplete", this, r, o);
11641             Roo.callback(o.success, o.scope, [r, o]);
11642             Roo.callback(o.callback, o.scope, [o, true, r]);
11643
11644             setTimeout(function(){document.body.removeChild(frame);}, 100);
11645         }
11646
11647         Roo.EventManager.on(frame, 'load', cb, this);
11648         form.submit();
11649
11650         if(hiddens){ // remove dynamic params
11651             for(var i = 0, len = hiddens.length; i < len; i++){
11652                 form.removeChild(hiddens[i]);
11653             }
11654         }
11655     }
11656 });
11657 /*
11658  * Based on:
11659  * Ext JS Library 1.1.1
11660  * Copyright(c) 2006-2007, Ext JS, LLC.
11661  *
11662  * Originally Released Under LGPL - original licence link has changed is not relivant.
11663  *
11664  * Fork - LGPL
11665  * <script type="text/javascript">
11666  */
11667  
11668 /**
11669  * Global Ajax request class.
11670  * 
11671  * @class Roo.Ajax
11672  * @extends Roo.data.Connection
11673  * @static
11674  * 
11675  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11676  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11677  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11678  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11679  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11680  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11681  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11682  */
11683 Roo.Ajax = new Roo.data.Connection({
11684     // fix up the docs
11685     /**
11686      * @scope Roo.Ajax
11687      * @type {Boolear} 
11688      */
11689     autoAbort : false,
11690
11691     /**
11692      * Serialize the passed form into a url encoded string
11693      * @scope Roo.Ajax
11694      * @param {String/HTMLElement} form
11695      * @return {String}
11696      */
11697     serializeForm : function(form){
11698         return Roo.lib.Ajax.serializeForm(form);
11699     }
11700 });/*
11701  * Based on:
11702  * Ext JS Library 1.1.1
11703  * Copyright(c) 2006-2007, Ext JS, LLC.
11704  *
11705  * Originally Released Under LGPL - original licence link has changed is not relivant.
11706  *
11707  * Fork - LGPL
11708  * <script type="text/javascript">
11709  */
11710
11711  
11712 /**
11713  * @class Roo.UpdateManager
11714  * @extends Roo.util.Observable
11715  * Provides AJAX-style update for Element object.<br><br>
11716  * Usage:<br>
11717  * <pre><code>
11718  * // Get it from a Roo.Element object
11719  * var el = Roo.get("foo");
11720  * var mgr = el.getUpdateManager();
11721  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11722  * ...
11723  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11724  * <br>
11725  * // or directly (returns the same UpdateManager instance)
11726  * var mgr = new Roo.UpdateManager("myElementId");
11727  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11728  * mgr.on("update", myFcnNeedsToKnow);
11729  * <br>
11730    // short handed call directly from the element object
11731    Roo.get("foo").load({
11732         url: "bar.php",
11733         scripts:true,
11734         params: "for=bar",
11735         text: "Loading Foo..."
11736    });
11737  * </code></pre>
11738  * @constructor
11739  * Create new UpdateManager directly.
11740  * @param {String/HTMLElement/Roo.Element} el The element to update
11741  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11742  */
11743 Roo.UpdateManager = function(el, forceNew){
11744     el = Roo.get(el);
11745     if(!forceNew && el.updateManager){
11746         return el.updateManager;
11747     }
11748     /**
11749      * The Element object
11750      * @type Roo.Element
11751      */
11752     this.el = el;
11753     /**
11754      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11755      * @type String
11756      */
11757     this.defaultUrl = null;
11758
11759     this.addEvents({
11760         /**
11761          * @event beforeupdate
11762          * Fired before an update is made, return false from your handler and the update is cancelled.
11763          * @param {Roo.Element} el
11764          * @param {String/Object/Function} url
11765          * @param {String/Object} params
11766          */
11767         "beforeupdate": true,
11768         /**
11769          * @event update
11770          * Fired after successful update is made.
11771          * @param {Roo.Element} el
11772          * @param {Object} oResponseObject The response Object
11773          */
11774         "update": true,
11775         /**
11776          * @event failure
11777          * Fired on update failure.
11778          * @param {Roo.Element} el
11779          * @param {Object} oResponseObject The response Object
11780          */
11781         "failure": true
11782     });
11783     var d = Roo.UpdateManager.defaults;
11784     /**
11785      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11786      * @type String
11787      */
11788     this.sslBlankUrl = d.sslBlankUrl;
11789     /**
11790      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11791      * @type Boolean
11792      */
11793     this.disableCaching = d.disableCaching;
11794     /**
11795      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11796      * @type String
11797      */
11798     this.indicatorText = d.indicatorText;
11799     /**
11800      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11801      * @type String
11802      */
11803     this.showLoadIndicator = d.showLoadIndicator;
11804     /**
11805      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11806      * @type Number
11807      */
11808     this.timeout = d.timeout;
11809
11810     /**
11811      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11812      * @type Boolean
11813      */
11814     this.loadScripts = d.loadScripts;
11815
11816     /**
11817      * Transaction object of current executing transaction
11818      */
11819     this.transaction = null;
11820
11821     /**
11822      * @private
11823      */
11824     this.autoRefreshProcId = null;
11825     /**
11826      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11827      * @type Function
11828      */
11829     this.refreshDelegate = this.refresh.createDelegate(this);
11830     /**
11831      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11832      * @type Function
11833      */
11834     this.updateDelegate = this.update.createDelegate(this);
11835     /**
11836      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11837      * @type Function
11838      */
11839     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11840     /**
11841      * @private
11842      */
11843     this.successDelegate = this.processSuccess.createDelegate(this);
11844     /**
11845      * @private
11846      */
11847     this.failureDelegate = this.processFailure.createDelegate(this);
11848
11849     if(!this.renderer){
11850      /**
11851       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11852       */
11853     this.renderer = new Roo.UpdateManager.BasicRenderer();
11854     }
11855     
11856     Roo.UpdateManager.superclass.constructor.call(this);
11857 };
11858
11859 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11860     /**
11861      * Get the Element this UpdateManager is bound to
11862      * @return {Roo.Element} The element
11863      */
11864     getEl : function(){
11865         return this.el;
11866     },
11867     /**
11868      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11869      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11870 <pre><code>
11871 um.update({<br/>
11872     url: "your-url.php",<br/>
11873     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11874     callback: yourFunction,<br/>
11875     scope: yourObject, //(optional scope)  <br/>
11876     discardUrl: false, <br/>
11877     nocache: false,<br/>
11878     text: "Loading...",<br/>
11879     timeout: 30,<br/>
11880     scripts: false<br/>
11881 });
11882 </code></pre>
11883      * The only required property is url. The optional properties nocache, text and scripts
11884      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11885      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11886      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11887      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11888      */
11889     update : function(url, params, callback, discardUrl){
11890         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11891             var method = this.method,
11892                 cfg;
11893             if(typeof url == "object"){ // must be config object
11894                 cfg = url;
11895                 url = cfg.url;
11896                 params = params || cfg.params;
11897                 callback = callback || cfg.callback;
11898                 discardUrl = discardUrl || cfg.discardUrl;
11899                 if(callback && cfg.scope){
11900                     callback = callback.createDelegate(cfg.scope);
11901                 }
11902                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11903                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11904                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11905                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11906                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11907             }
11908             this.showLoading();
11909             if(!discardUrl){
11910                 this.defaultUrl = url;
11911             }
11912             if(typeof url == "function"){
11913                 url = url.call(this);
11914             }
11915
11916             method = method || (params ? "POST" : "GET");
11917             if(method == "GET"){
11918                 url = this.prepareUrl(url);
11919             }
11920
11921             var o = Roo.apply(cfg ||{}, {
11922                 url : url,
11923                 params: params,
11924                 success: this.successDelegate,
11925                 failure: this.failureDelegate,
11926                 callback: undefined,
11927                 timeout: (this.timeout*1000),
11928                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11929             });
11930             Roo.log("updated manager called with timeout of " + o.timeout);
11931             this.transaction = Roo.Ajax.request(o);
11932         }
11933     },
11934
11935     /**
11936      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11937      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11938      * @param {String/HTMLElement} form The form Id or form element
11939      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11940      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11941      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11942      */
11943     formUpdate : function(form, url, reset, callback){
11944         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11945             if(typeof url == "function"){
11946                 url = url.call(this);
11947             }
11948             form = Roo.getDom(form);
11949             this.transaction = Roo.Ajax.request({
11950                 form: form,
11951                 url:url,
11952                 success: this.successDelegate,
11953                 failure: this.failureDelegate,
11954                 timeout: (this.timeout*1000),
11955                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11956             });
11957             this.showLoading.defer(1, this);
11958         }
11959     },
11960
11961     /**
11962      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11963      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11964      */
11965     refresh : function(callback){
11966         if(this.defaultUrl == null){
11967             return;
11968         }
11969         this.update(this.defaultUrl, null, callback, true);
11970     },
11971
11972     /**
11973      * Set this element to auto refresh.
11974      * @param {Number} interval How often to update (in seconds).
11975      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11976      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11977      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11978      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11979      */
11980     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11981         if(refreshNow){
11982             this.update(url || this.defaultUrl, params, callback, true);
11983         }
11984         if(this.autoRefreshProcId){
11985             clearInterval(this.autoRefreshProcId);
11986         }
11987         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11988     },
11989
11990     /**
11991      * Stop auto refresh on this element.
11992      */
11993      stopAutoRefresh : function(){
11994         if(this.autoRefreshProcId){
11995             clearInterval(this.autoRefreshProcId);
11996             delete this.autoRefreshProcId;
11997         }
11998     },
11999
12000     isAutoRefreshing : function(){
12001        return this.autoRefreshProcId ? true : false;
12002     },
12003     /**
12004      * Called to update the element to "Loading" state. Override to perform custom action.
12005      */
12006     showLoading : function(){
12007         if(this.showLoadIndicator){
12008             this.el.update(this.indicatorText);
12009         }
12010     },
12011
12012     /**
12013      * Adds unique parameter to query string if disableCaching = true
12014      * @private
12015      */
12016     prepareUrl : function(url){
12017         if(this.disableCaching){
12018             var append = "_dc=" + (new Date().getTime());
12019             if(url.indexOf("?") !== -1){
12020                 url += "&" + append;
12021             }else{
12022                 url += "?" + append;
12023             }
12024         }
12025         return url;
12026     },
12027
12028     /**
12029      * @private
12030      */
12031     processSuccess : function(response){
12032         this.transaction = null;
12033         if(response.argument.form && response.argument.reset){
12034             try{ // put in try/catch since some older FF releases had problems with this
12035                 response.argument.form.reset();
12036             }catch(e){}
12037         }
12038         if(this.loadScripts){
12039             this.renderer.render(this.el, response, this,
12040                 this.updateComplete.createDelegate(this, [response]));
12041         }else{
12042             this.renderer.render(this.el, response, this);
12043             this.updateComplete(response);
12044         }
12045     },
12046
12047     updateComplete : function(response){
12048         this.fireEvent("update", this.el, response);
12049         if(typeof response.argument.callback == "function"){
12050             response.argument.callback(this.el, true, response);
12051         }
12052     },
12053
12054     /**
12055      * @private
12056      */
12057     processFailure : function(response){
12058         this.transaction = null;
12059         this.fireEvent("failure", this.el, response);
12060         if(typeof response.argument.callback == "function"){
12061             response.argument.callback(this.el, false, response);
12062         }
12063     },
12064
12065     /**
12066      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12067      * @param {Object} renderer The object implementing the render() method
12068      */
12069     setRenderer : function(renderer){
12070         this.renderer = renderer;
12071     },
12072
12073     getRenderer : function(){
12074        return this.renderer;
12075     },
12076
12077     /**
12078      * Set the defaultUrl used for updates
12079      * @param {String/Function} defaultUrl The url or a function to call to get the url
12080      */
12081     setDefaultUrl : function(defaultUrl){
12082         this.defaultUrl = defaultUrl;
12083     },
12084
12085     /**
12086      * Aborts the executing transaction
12087      */
12088     abort : function(){
12089         if(this.transaction){
12090             Roo.Ajax.abort(this.transaction);
12091         }
12092     },
12093
12094     /**
12095      * Returns true if an update is in progress
12096      * @return {Boolean}
12097      */
12098     isUpdating : function(){
12099         if(this.transaction){
12100             return Roo.Ajax.isLoading(this.transaction);
12101         }
12102         return false;
12103     }
12104 });
12105
12106 /**
12107  * @class Roo.UpdateManager.defaults
12108  * @static (not really - but it helps the doc tool)
12109  * The defaults collection enables customizing the default properties of UpdateManager
12110  */
12111    Roo.UpdateManager.defaults = {
12112        /**
12113          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12114          * @type Number
12115          */
12116          timeout : 30,
12117
12118          /**
12119          * True to process scripts by default (Defaults to false).
12120          * @type Boolean
12121          */
12122         loadScripts : false,
12123
12124         /**
12125         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12126         * @type String
12127         */
12128         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12129         /**
12130          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12131          * @type Boolean
12132          */
12133         disableCaching : false,
12134         /**
12135          * Whether to show indicatorText when loading (Defaults to true).
12136          * @type Boolean
12137          */
12138         showLoadIndicator : true,
12139         /**
12140          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12141          * @type String
12142          */
12143         indicatorText : '<div class="loading-indicator">Loading...</div>'
12144    };
12145
12146 /**
12147  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12148  *Usage:
12149  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12150  * @param {String/HTMLElement/Roo.Element} el The element to update
12151  * @param {String} url The url
12152  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12153  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12154  * @static
12155  * @deprecated
12156  * @member Roo.UpdateManager
12157  */
12158 Roo.UpdateManager.updateElement = function(el, url, params, options){
12159     var um = Roo.get(el, true).getUpdateManager();
12160     Roo.apply(um, options);
12161     um.update(url, params, options ? options.callback : null);
12162 };
12163 // alias for backwards compat
12164 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12165 /**
12166  * @class Roo.UpdateManager.BasicRenderer
12167  * Default Content renderer. Updates the elements innerHTML with the responseText.
12168  */
12169 Roo.UpdateManager.BasicRenderer = function(){};
12170
12171 Roo.UpdateManager.BasicRenderer.prototype = {
12172     /**
12173      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12174      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12175      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12176      * @param {Roo.Element} el The element being rendered
12177      * @param {Object} response The YUI Connect response object
12178      * @param {UpdateManager} updateManager The calling update manager
12179      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12180      */
12181      render : function(el, response, updateManager, callback){
12182         el.update(response.responseText, updateManager.loadScripts, callback);
12183     }
12184 };
12185 /*
12186  * Based on:
12187  * Roo JS
12188  * (c)) Alan Knowles
12189  * Licence : LGPL
12190  */
12191
12192
12193 /**
12194  * @class Roo.DomTemplate
12195  * @extends Roo.Template
12196  * An effort at a dom based template engine..
12197  *
12198  * Similar to XTemplate, except it uses dom parsing to create the template..
12199  *
12200  * Supported features:
12201  *
12202  *  Tags:
12203
12204 <pre><code>
12205       {a_variable} - output encoded.
12206       {a_variable.format:("Y-m-d")} - call a method on the variable
12207       {a_variable:raw} - unencoded output
12208       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12209       {a_variable:this.method_on_template(...)} - call a method on the template object.
12210  
12211 </code></pre>
12212  *  The tpl tag:
12213 <pre><code>
12214         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12215         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12216         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12217         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12218   
12219 </code></pre>
12220  *      
12221  */
12222 Roo.DomTemplate = function()
12223 {
12224      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12225      if (this.html) {
12226         this.compile();
12227      }
12228 };
12229
12230
12231 Roo.extend(Roo.DomTemplate, Roo.Template, {
12232     /**
12233      * id counter for sub templates.
12234      */
12235     id : 0,
12236     /**
12237      * flag to indicate if dom parser is inside a pre,
12238      * it will strip whitespace if not.
12239      */
12240     inPre : false,
12241     
12242     /**
12243      * The various sub templates
12244      */
12245     tpls : false,
12246     
12247     
12248     
12249     /**
12250      *
12251      * basic tag replacing syntax
12252      * WORD:WORD()
12253      *
12254      * // you can fake an object call by doing this
12255      *  x.t:(test,tesT) 
12256      * 
12257      */
12258     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12259     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12260     
12261     iterChild : function (node, method) {
12262         
12263         var oldPre = this.inPre;
12264         if (node.tagName == 'PRE') {
12265             this.inPre = true;
12266         }
12267         for( var i = 0; i < node.childNodes.length; i++) {
12268             method.call(this, node.childNodes[i]);
12269         }
12270         this.inPre = oldPre;
12271     },
12272     
12273     
12274     
12275     /**
12276      * compile the template
12277      *
12278      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12279      *
12280      */
12281     compile: function()
12282     {
12283         var s = this.html;
12284         
12285         // covert the html into DOM...
12286         var doc = false;
12287         var div =false;
12288         try {
12289             doc = document.implementation.createHTMLDocument("");
12290             doc.documentElement.innerHTML =   this.html  ;
12291             div = doc.documentElement;
12292         } catch (e) {
12293             // old IE... - nasty -- it causes all sorts of issues.. with
12294             // images getting pulled from server..
12295             div = document.createElement('div');
12296             div.innerHTML = this.html;
12297         }
12298         //doc.documentElement.innerHTML = htmlBody
12299          
12300         
12301         
12302         this.tpls = [];
12303         var _t = this;
12304         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12305         
12306         var tpls = this.tpls;
12307         
12308         // create a top level template from the snippet..
12309         
12310         //Roo.log(div.innerHTML);
12311         
12312         var tpl = {
12313             uid : 'master',
12314             id : this.id++,
12315             attr : false,
12316             value : false,
12317             body : div.innerHTML,
12318             
12319             forCall : false,
12320             execCall : false,
12321             dom : div,
12322             isTop : true
12323             
12324         };
12325         tpls.unshift(tpl);
12326         
12327         
12328         // compile them...
12329         this.tpls = [];
12330         Roo.each(tpls, function(tp){
12331             this.compileTpl(tp);
12332             this.tpls[tp.id] = tp;
12333         }, this);
12334         
12335         this.master = tpls[0];
12336         return this;
12337         
12338         
12339     },
12340     
12341     compileNode : function(node, istop) {
12342         // test for
12343         //Roo.log(node);
12344         
12345         
12346         // skip anything not a tag..
12347         if (node.nodeType != 1) {
12348             if (node.nodeType == 3 && !this.inPre) {
12349                 // reduce white space..
12350                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12351                 
12352             }
12353             return;
12354         }
12355         
12356         var tpl = {
12357             uid : false,
12358             id : false,
12359             attr : false,
12360             value : false,
12361             body : '',
12362             
12363             forCall : false,
12364             execCall : false,
12365             dom : false,
12366             isTop : istop
12367             
12368             
12369         };
12370         
12371         
12372         switch(true) {
12373             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12374             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12375             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12376             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12377             // no default..
12378         }
12379         
12380         
12381         if (!tpl.attr) {
12382             // just itterate children..
12383             this.iterChild(node,this.compileNode);
12384             return;
12385         }
12386         tpl.uid = this.id++;
12387         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12388         node.removeAttribute('roo-'+ tpl.attr);
12389         if (tpl.attr != 'name') {
12390             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12391             node.parentNode.replaceChild(placeholder,  node);
12392         } else {
12393             
12394             var placeholder =  document.createElement('span');
12395             placeholder.className = 'roo-tpl-' + tpl.value;
12396             node.parentNode.replaceChild(placeholder,  node);
12397         }
12398         
12399         // parent now sees '{domtplXXXX}
12400         this.iterChild(node,this.compileNode);
12401         
12402         // we should now have node body...
12403         var div = document.createElement('div');
12404         div.appendChild(node);
12405         tpl.dom = node;
12406         // this has the unfortunate side effect of converting tagged attributes
12407         // eg. href="{...}" into %7C...%7D
12408         // this has been fixed by searching for those combo's although it's a bit hacky..
12409         
12410         
12411         tpl.body = div.innerHTML;
12412         
12413         
12414          
12415         tpl.id = tpl.uid;
12416         switch(tpl.attr) {
12417             case 'for' :
12418                 switch (tpl.value) {
12419                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12420                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12421                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12422                 }
12423                 break;
12424             
12425             case 'exec':
12426                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12427                 break;
12428             
12429             case 'if':     
12430                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12431                 break;
12432             
12433             case 'name':
12434                 tpl.id  = tpl.value; // replace non characters???
12435                 break;
12436             
12437         }
12438         
12439         
12440         this.tpls.push(tpl);
12441         
12442         
12443         
12444     },
12445     
12446     
12447     
12448     
12449     /**
12450      * Compile a segment of the template into a 'sub-template'
12451      *
12452      * 
12453      * 
12454      *
12455      */
12456     compileTpl : function(tpl)
12457     {
12458         var fm = Roo.util.Format;
12459         var useF = this.disableFormats !== true;
12460         
12461         var sep = Roo.isGecko ? "+\n" : ",\n";
12462         
12463         var undef = function(str) {
12464             Roo.debug && Roo.log("Property not found :"  + str);
12465             return '';
12466         };
12467           
12468         //Roo.log(tpl.body);
12469         
12470         
12471         
12472         var fn = function(m, lbrace, name, format, args)
12473         {
12474             //Roo.log("ARGS");
12475             //Roo.log(arguments);
12476             args = args ? args.replace(/\\'/g,"'") : args;
12477             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12478             if (typeof(format) == 'undefined') {
12479                 format =  'htmlEncode'; 
12480             }
12481             if (format == 'raw' ) {
12482                 format = false;
12483             }
12484             
12485             if(name.substr(0, 6) == 'domtpl'){
12486                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12487             }
12488             
12489             // build an array of options to determine if value is undefined..
12490             
12491             // basically get 'xxxx.yyyy' then do
12492             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12493             //    (function () { Roo.log("Property not found"); return ''; })() :
12494             //    ......
12495             
12496             var udef_ar = [];
12497             var lookfor = '';
12498             Roo.each(name.split('.'), function(st) {
12499                 lookfor += (lookfor.length ? '.': '') + st;
12500                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12501             });
12502             
12503             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12504             
12505             
12506             if(format && useF){
12507                 
12508                 args = args ? ',' + args : "";
12509                  
12510                 if(format.substr(0, 5) != "this."){
12511                     format = "fm." + format + '(';
12512                 }else{
12513                     format = 'this.call("'+ format.substr(5) + '", ';
12514                     args = ", values";
12515                 }
12516                 
12517                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12518             }
12519              
12520             if (args && args.length) {
12521                 // called with xxyx.yuu:(test,test)
12522                 // change to ()
12523                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12524             }
12525             // raw.. - :raw modifier..
12526             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12527             
12528         };
12529         var body;
12530         // branched to use + in gecko and [].join() in others
12531         if(Roo.isGecko){
12532             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12533                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12534                     "';};};";
12535         }else{
12536             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12537             body.push(tpl.body.replace(/(\r\n|\n)/g,
12538                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12539             body.push("'].join('');};};");
12540             body = body.join('');
12541         }
12542         
12543         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12544        
12545         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12546         eval(body);
12547         
12548         return this;
12549     },
12550      
12551     /**
12552      * same as applyTemplate, except it's done to one of the subTemplates
12553      * when using named templates, you can do:
12554      *
12555      * var str = pl.applySubTemplate('your-name', values);
12556      *
12557      * 
12558      * @param {Number} id of the template
12559      * @param {Object} values to apply to template
12560      * @param {Object} parent (normaly the instance of this object)
12561      */
12562     applySubTemplate : function(id, values, parent)
12563     {
12564         
12565         
12566         var t = this.tpls[id];
12567         
12568         
12569         try { 
12570             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12571                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12572                 return '';
12573             }
12574         } catch(e) {
12575             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12576             Roo.log(values);
12577           
12578             return '';
12579         }
12580         try { 
12581             
12582             if(t.execCall && t.execCall.call(this, values, parent)){
12583                 return '';
12584             }
12585         } catch(e) {
12586             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12587             Roo.log(values);
12588             return '';
12589         }
12590         
12591         try {
12592             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12593             parent = t.target ? values : parent;
12594             if(t.forCall && vs instanceof Array){
12595                 var buf = [];
12596                 for(var i = 0, len = vs.length; i < len; i++){
12597                     try {
12598                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12599                     } catch (e) {
12600                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12601                         Roo.log(e.body);
12602                         //Roo.log(t.compiled);
12603                         Roo.log(vs[i]);
12604                     }   
12605                 }
12606                 return buf.join('');
12607             }
12608         } catch (e) {
12609             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12610             Roo.log(values);
12611             return '';
12612         }
12613         try {
12614             return t.compiled.call(this, vs, parent);
12615         } catch (e) {
12616             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12617             Roo.log(e.body);
12618             //Roo.log(t.compiled);
12619             Roo.log(values);
12620             return '';
12621         }
12622     },
12623
12624    
12625
12626     applyTemplate : function(values){
12627         return this.master.compiled.call(this, values, {});
12628         //var s = this.subs;
12629     },
12630
12631     apply : function(){
12632         return this.applyTemplate.apply(this, arguments);
12633     }
12634
12635  });
12636
12637 Roo.DomTemplate.from = function(el){
12638     el = Roo.getDom(el);
12639     return new Roo.Domtemplate(el.value || el.innerHTML);
12640 };/*
12641  * Based on:
12642  * Ext JS Library 1.1.1
12643  * Copyright(c) 2006-2007, Ext JS, LLC.
12644  *
12645  * Originally Released Under LGPL - original licence link has changed is not relivant.
12646  *
12647  * Fork - LGPL
12648  * <script type="text/javascript">
12649  */
12650
12651 /**
12652  * @class Roo.util.DelayedTask
12653  * Provides a convenient method of performing setTimeout where a new
12654  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12655  * You can use this class to buffer
12656  * the keypress events for a certain number of milliseconds, and perform only if they stop
12657  * for that amount of time.
12658  * @constructor The parameters to this constructor serve as defaults and are not required.
12659  * @param {Function} fn (optional) The default function to timeout
12660  * @param {Object} scope (optional) The default scope of that timeout
12661  * @param {Array} args (optional) The default Array of arguments
12662  */
12663 Roo.util.DelayedTask = function(fn, scope, args){
12664     var id = null, d, t;
12665
12666     var call = function(){
12667         var now = new Date().getTime();
12668         if(now - t >= d){
12669             clearInterval(id);
12670             id = null;
12671             fn.apply(scope, args || []);
12672         }
12673     };
12674     /**
12675      * Cancels any pending timeout and queues a new one
12676      * @param {Number} delay The milliseconds to delay
12677      * @param {Function} newFn (optional) Overrides function passed to constructor
12678      * @param {Object} newScope (optional) Overrides scope passed to constructor
12679      * @param {Array} newArgs (optional) Overrides args passed to constructor
12680      */
12681     this.delay = function(delay, newFn, newScope, newArgs){
12682         if(id && delay != d){
12683             this.cancel();
12684         }
12685         d = delay;
12686         t = new Date().getTime();
12687         fn = newFn || fn;
12688         scope = newScope || scope;
12689         args = newArgs || args;
12690         if(!id){
12691             id = setInterval(call, d);
12692         }
12693     };
12694
12695     /**
12696      * Cancel the last queued timeout
12697      */
12698     this.cancel = function(){
12699         if(id){
12700             clearInterval(id);
12701             id = null;
12702         }
12703     };
12704 };/*
12705  * Based on:
12706  * Ext JS Library 1.1.1
12707  * Copyright(c) 2006-2007, Ext JS, LLC.
12708  *
12709  * Originally Released Under LGPL - original licence link has changed is not relivant.
12710  *
12711  * Fork - LGPL
12712  * <script type="text/javascript">
12713  */
12714  
12715  
12716 Roo.util.TaskRunner = function(interval){
12717     interval = interval || 10;
12718     var tasks = [], removeQueue = [];
12719     var id = 0;
12720     var running = false;
12721
12722     var stopThread = function(){
12723         running = false;
12724         clearInterval(id);
12725         id = 0;
12726     };
12727
12728     var startThread = function(){
12729         if(!running){
12730             running = true;
12731             id = setInterval(runTasks, interval);
12732         }
12733     };
12734
12735     var removeTask = function(task){
12736         removeQueue.push(task);
12737         if(task.onStop){
12738             task.onStop();
12739         }
12740     };
12741
12742     var runTasks = function(){
12743         if(removeQueue.length > 0){
12744             for(var i = 0, len = removeQueue.length; i < len; i++){
12745                 tasks.remove(removeQueue[i]);
12746             }
12747             removeQueue = [];
12748             if(tasks.length < 1){
12749                 stopThread();
12750                 return;
12751             }
12752         }
12753         var now = new Date().getTime();
12754         for(var i = 0, len = tasks.length; i < len; ++i){
12755             var t = tasks[i];
12756             var itime = now - t.taskRunTime;
12757             if(t.interval <= itime){
12758                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12759                 t.taskRunTime = now;
12760                 if(rt === false || t.taskRunCount === t.repeat){
12761                     removeTask(t);
12762                     return;
12763                 }
12764             }
12765             if(t.duration && t.duration <= (now - t.taskStartTime)){
12766                 removeTask(t);
12767             }
12768         }
12769     };
12770
12771     /**
12772      * Queues a new task.
12773      * @param {Object} task
12774      */
12775     this.start = function(task){
12776         tasks.push(task);
12777         task.taskStartTime = new Date().getTime();
12778         task.taskRunTime = 0;
12779         task.taskRunCount = 0;
12780         startThread();
12781         return task;
12782     };
12783
12784     this.stop = function(task){
12785         removeTask(task);
12786         return task;
12787     };
12788
12789     this.stopAll = function(){
12790         stopThread();
12791         for(var i = 0, len = tasks.length; i < len; i++){
12792             if(tasks[i].onStop){
12793                 tasks[i].onStop();
12794             }
12795         }
12796         tasks = [];
12797         removeQueue = [];
12798     };
12799 };
12800
12801 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12802  * Based on:
12803  * Ext JS Library 1.1.1
12804  * Copyright(c) 2006-2007, Ext JS, LLC.
12805  *
12806  * Originally Released Under LGPL - original licence link has changed is not relivant.
12807  *
12808  * Fork - LGPL
12809  * <script type="text/javascript">
12810  */
12811
12812  
12813 /**
12814  * @class Roo.util.MixedCollection
12815  * @extends Roo.util.Observable
12816  * A Collection class that maintains both numeric indexes and keys and exposes events.
12817  * @constructor
12818  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12819  * collection (defaults to false)
12820  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12821  * and return the key value for that item.  This is used when available to look up the key on items that
12822  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12823  * equivalent to providing an implementation for the {@link #getKey} method.
12824  */
12825 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12826     this.items = [];
12827     this.map = {};
12828     this.keys = [];
12829     this.length = 0;
12830     this.addEvents({
12831         /**
12832          * @event clear
12833          * Fires when the collection is cleared.
12834          */
12835         "clear" : true,
12836         /**
12837          * @event add
12838          * Fires when an item is added to the collection.
12839          * @param {Number} index The index at which the item was added.
12840          * @param {Object} o The item added.
12841          * @param {String} key The key associated with the added item.
12842          */
12843         "add" : true,
12844         /**
12845          * @event replace
12846          * Fires when an item is replaced in the collection.
12847          * @param {String} key he key associated with the new added.
12848          * @param {Object} old The item being replaced.
12849          * @param {Object} new The new item.
12850          */
12851         "replace" : true,
12852         /**
12853          * @event remove
12854          * Fires when an item is removed from the collection.
12855          * @param {Object} o The item being removed.
12856          * @param {String} key (optional) The key associated with the removed item.
12857          */
12858         "remove" : true,
12859         "sort" : true
12860     });
12861     this.allowFunctions = allowFunctions === true;
12862     if(keyFn){
12863         this.getKey = keyFn;
12864     }
12865     Roo.util.MixedCollection.superclass.constructor.call(this);
12866 };
12867
12868 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12869     allowFunctions : false,
12870     
12871 /**
12872  * Adds an item to the collection.
12873  * @param {String} key The key to associate with the item
12874  * @param {Object} o The item to add.
12875  * @return {Object} The item added.
12876  */
12877     add : function(key, o){
12878         if(arguments.length == 1){
12879             o = arguments[0];
12880             key = this.getKey(o);
12881         }
12882         if(typeof key == "undefined" || key === null){
12883             this.length++;
12884             this.items.push(o);
12885             this.keys.push(null);
12886         }else{
12887             var old = this.map[key];
12888             if(old){
12889                 return this.replace(key, o);
12890             }
12891             this.length++;
12892             this.items.push(o);
12893             this.map[key] = o;
12894             this.keys.push(key);
12895         }
12896         this.fireEvent("add", this.length-1, o, key);
12897         return o;
12898     },
12899        
12900 /**
12901   * MixedCollection has a generic way to fetch keys if you implement getKey.
12902 <pre><code>
12903 // normal way
12904 var mc = new Roo.util.MixedCollection();
12905 mc.add(someEl.dom.id, someEl);
12906 mc.add(otherEl.dom.id, otherEl);
12907 //and so on
12908
12909 // using getKey
12910 var mc = new Roo.util.MixedCollection();
12911 mc.getKey = function(el){
12912    return el.dom.id;
12913 };
12914 mc.add(someEl);
12915 mc.add(otherEl);
12916
12917 // or via the constructor
12918 var mc = new Roo.util.MixedCollection(false, function(el){
12919    return el.dom.id;
12920 });
12921 mc.add(someEl);
12922 mc.add(otherEl);
12923 </code></pre>
12924  * @param o {Object} The item for which to find the key.
12925  * @return {Object} The key for the passed item.
12926  */
12927     getKey : function(o){
12928          return o.id; 
12929     },
12930    
12931 /**
12932  * Replaces an item in the collection.
12933  * @param {String} key The key associated with the item to replace, or the item to replace.
12934  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12935  * @return {Object}  The new item.
12936  */
12937     replace : function(key, o){
12938         if(arguments.length == 1){
12939             o = arguments[0];
12940             key = this.getKey(o);
12941         }
12942         var old = this.item(key);
12943         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12944              return this.add(key, o);
12945         }
12946         var index = this.indexOfKey(key);
12947         this.items[index] = o;
12948         this.map[key] = o;
12949         this.fireEvent("replace", key, old, o);
12950         return o;
12951     },
12952    
12953 /**
12954  * Adds all elements of an Array or an Object to the collection.
12955  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12956  * an Array of values, each of which are added to the collection.
12957  */
12958     addAll : function(objs){
12959         if(arguments.length > 1 || objs instanceof Array){
12960             var args = arguments.length > 1 ? arguments : objs;
12961             for(var i = 0, len = args.length; i < len; i++){
12962                 this.add(args[i]);
12963             }
12964         }else{
12965             for(var key in objs){
12966                 if(this.allowFunctions || typeof objs[key] != "function"){
12967                     this.add(key, objs[key]);
12968                 }
12969             }
12970         }
12971     },
12972    
12973 /**
12974  * Executes the specified function once for every item in the collection, passing each
12975  * item as the first and only parameter. returning false from the function will stop the iteration.
12976  * @param {Function} fn The function to execute for each item.
12977  * @param {Object} scope (optional) The scope in which to execute the function.
12978  */
12979     each : function(fn, scope){
12980         var items = [].concat(this.items); // each safe for removal
12981         for(var i = 0, len = items.length; i < len; i++){
12982             if(fn.call(scope || items[i], items[i], i, len) === false){
12983                 break;
12984             }
12985         }
12986     },
12987    
12988 /**
12989  * Executes the specified function once for every key in the collection, passing each
12990  * key, and its associated item as the first two parameters.
12991  * @param {Function} fn The function to execute for each item.
12992  * @param {Object} scope (optional) The scope in which to execute the function.
12993  */
12994     eachKey : function(fn, scope){
12995         for(var i = 0, len = this.keys.length; i < len; i++){
12996             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12997         }
12998     },
12999    
13000 /**
13001  * Returns the first item in the collection which elicits a true return value from the
13002  * passed selection function.
13003  * @param {Function} fn The selection function to execute for each item.
13004  * @param {Object} scope (optional) The scope in which to execute the function.
13005  * @return {Object} The first item in the collection which returned true from the selection function.
13006  */
13007     find : function(fn, scope){
13008         for(var i = 0, len = this.items.length; i < len; i++){
13009             if(fn.call(scope || window, this.items[i], this.keys[i])){
13010                 return this.items[i];
13011             }
13012         }
13013         return null;
13014     },
13015    
13016 /**
13017  * Inserts an item at the specified index in the collection.
13018  * @param {Number} index The index to insert the item at.
13019  * @param {String} key The key to associate with the new item, or the item itself.
13020  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13021  * @return {Object} The item inserted.
13022  */
13023     insert : function(index, key, o){
13024         if(arguments.length == 2){
13025             o = arguments[1];
13026             key = this.getKey(o);
13027         }
13028         if(index >= this.length){
13029             return this.add(key, o);
13030         }
13031         this.length++;
13032         this.items.splice(index, 0, o);
13033         if(typeof key != "undefined" && key != null){
13034             this.map[key] = o;
13035         }
13036         this.keys.splice(index, 0, key);
13037         this.fireEvent("add", index, o, key);
13038         return o;
13039     },
13040    
13041 /**
13042  * Removed an item from the collection.
13043  * @param {Object} o The item to remove.
13044  * @return {Object} The item removed.
13045  */
13046     remove : function(o){
13047         return this.removeAt(this.indexOf(o));
13048     },
13049    
13050 /**
13051  * Remove an item from a specified index in the collection.
13052  * @param {Number} index The index within the collection of the item to remove.
13053  */
13054     removeAt : function(index){
13055         if(index < this.length && index >= 0){
13056             this.length--;
13057             var o = this.items[index];
13058             this.items.splice(index, 1);
13059             var key = this.keys[index];
13060             if(typeof key != "undefined"){
13061                 delete this.map[key];
13062             }
13063             this.keys.splice(index, 1);
13064             this.fireEvent("remove", o, key);
13065         }
13066     },
13067    
13068 /**
13069  * Removed an item associated with the passed key fom the collection.
13070  * @param {String} key The key of the item to remove.
13071  */
13072     removeKey : function(key){
13073         return this.removeAt(this.indexOfKey(key));
13074     },
13075    
13076 /**
13077  * Returns the number of items in the collection.
13078  * @return {Number} the number of items in the collection.
13079  */
13080     getCount : function(){
13081         return this.length; 
13082     },
13083    
13084 /**
13085  * Returns index within the collection of the passed Object.
13086  * @param {Object} o The item to find the index of.
13087  * @return {Number} index of the item.
13088  */
13089     indexOf : function(o){
13090         if(!this.items.indexOf){
13091             for(var i = 0, len = this.items.length; i < len; i++){
13092                 if(this.items[i] == o) return i;
13093             }
13094             return -1;
13095         }else{
13096             return this.items.indexOf(o);
13097         }
13098     },
13099    
13100 /**
13101  * Returns index within the collection of the passed key.
13102  * @param {String} key The key to find the index of.
13103  * @return {Number} index of the key.
13104  */
13105     indexOfKey : function(key){
13106         if(!this.keys.indexOf){
13107             for(var i = 0, len = this.keys.length; i < len; i++){
13108                 if(this.keys[i] == key) return i;
13109             }
13110             return -1;
13111         }else{
13112             return this.keys.indexOf(key);
13113         }
13114     },
13115    
13116 /**
13117  * Returns the item associated with the passed key OR index. Key has priority over index.
13118  * @param {String/Number} key The key or index of the item.
13119  * @return {Object} The item associated with the passed key.
13120  */
13121     item : function(key){
13122         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13123         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13124     },
13125     
13126 /**
13127  * Returns the item at the specified index.
13128  * @param {Number} index The index of the item.
13129  * @return {Object}
13130  */
13131     itemAt : function(index){
13132         return this.items[index];
13133     },
13134     
13135 /**
13136  * Returns the item associated with the passed key.
13137  * @param {String/Number} key The key of the item.
13138  * @return {Object} The item associated with the passed key.
13139  */
13140     key : function(key){
13141         return this.map[key];
13142     },
13143    
13144 /**
13145  * Returns true if the collection contains the passed Object as an item.
13146  * @param {Object} o  The Object to look for in the collection.
13147  * @return {Boolean} True if the collection contains the Object as an item.
13148  */
13149     contains : function(o){
13150         return this.indexOf(o) != -1;
13151     },
13152    
13153 /**
13154  * Returns true if the collection contains the passed Object as a key.
13155  * @param {String} key The key to look for in the collection.
13156  * @return {Boolean} True if the collection contains the Object as a key.
13157  */
13158     containsKey : function(key){
13159         return typeof this.map[key] != "undefined";
13160     },
13161    
13162 /**
13163  * Removes all items from the collection.
13164  */
13165     clear : function(){
13166         this.length = 0;
13167         this.items = [];
13168         this.keys = [];
13169         this.map = {};
13170         this.fireEvent("clear");
13171     },
13172    
13173 /**
13174  * Returns the first item in the collection.
13175  * @return {Object} the first item in the collection..
13176  */
13177     first : function(){
13178         return this.items[0]; 
13179     },
13180    
13181 /**
13182  * Returns the last item in the collection.
13183  * @return {Object} the last item in the collection..
13184  */
13185     last : function(){
13186         return this.items[this.length-1];   
13187     },
13188     
13189     _sort : function(property, dir, fn){
13190         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13191         fn = fn || function(a, b){
13192             return a-b;
13193         };
13194         var c = [], k = this.keys, items = this.items;
13195         for(var i = 0, len = items.length; i < len; i++){
13196             c[c.length] = {key: k[i], value: items[i], index: i};
13197         }
13198         c.sort(function(a, b){
13199             var v = fn(a[property], b[property]) * dsc;
13200             if(v == 0){
13201                 v = (a.index < b.index ? -1 : 1);
13202             }
13203             return v;
13204         });
13205         for(var i = 0, len = c.length; i < len; i++){
13206             items[i] = c[i].value;
13207             k[i] = c[i].key;
13208         }
13209         this.fireEvent("sort", this);
13210     },
13211     
13212     /**
13213      * Sorts this collection with the passed comparison function
13214      * @param {String} direction (optional) "ASC" or "DESC"
13215      * @param {Function} fn (optional) comparison function
13216      */
13217     sort : function(dir, fn){
13218         this._sort("value", dir, fn);
13219     },
13220     
13221     /**
13222      * Sorts this collection by keys
13223      * @param {String} direction (optional) "ASC" or "DESC"
13224      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13225      */
13226     keySort : function(dir, fn){
13227         this._sort("key", dir, fn || function(a, b){
13228             return String(a).toUpperCase()-String(b).toUpperCase();
13229         });
13230     },
13231     
13232     /**
13233      * Returns a range of items in this collection
13234      * @param {Number} startIndex (optional) defaults to 0
13235      * @param {Number} endIndex (optional) default to the last item
13236      * @return {Array} An array of items
13237      */
13238     getRange : function(start, end){
13239         var items = this.items;
13240         if(items.length < 1){
13241             return [];
13242         }
13243         start = start || 0;
13244         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13245         var r = [];
13246         if(start <= end){
13247             for(var i = start; i <= end; i++) {
13248                     r[r.length] = items[i];
13249             }
13250         }else{
13251             for(var i = start; i >= end; i--) {
13252                     r[r.length] = items[i];
13253             }
13254         }
13255         return r;
13256     },
13257         
13258     /**
13259      * Filter the <i>objects</i> in this collection by a specific property. 
13260      * Returns a new collection that has been filtered.
13261      * @param {String} property A property on your objects
13262      * @param {String/RegExp} value Either string that the property values 
13263      * should start with or a RegExp to test against the property
13264      * @return {MixedCollection} The new filtered collection
13265      */
13266     filter : function(property, value){
13267         if(!value.exec){ // not a regex
13268             value = String(value);
13269             if(value.length == 0){
13270                 return this.clone();
13271             }
13272             value = new RegExp("^" + Roo.escapeRe(value), "i");
13273         }
13274         return this.filterBy(function(o){
13275             return o && value.test(o[property]);
13276         });
13277         },
13278     
13279     /**
13280      * Filter by a function. * Returns a new collection that has been filtered.
13281      * The passed function will be called with each 
13282      * object in the collection. If the function returns true, the value is included 
13283      * otherwise it is filtered.
13284      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13285      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13286      * @return {MixedCollection} The new filtered collection
13287      */
13288     filterBy : function(fn, scope){
13289         var r = new Roo.util.MixedCollection();
13290         r.getKey = this.getKey;
13291         var k = this.keys, it = this.items;
13292         for(var i = 0, len = it.length; i < len; i++){
13293             if(fn.call(scope||this, it[i], k[i])){
13294                                 r.add(k[i], it[i]);
13295                         }
13296         }
13297         return r;
13298     },
13299     
13300     /**
13301      * Creates a duplicate of this collection
13302      * @return {MixedCollection}
13303      */
13304     clone : function(){
13305         var r = new Roo.util.MixedCollection();
13306         var k = this.keys, it = this.items;
13307         for(var i = 0, len = it.length; i < len; i++){
13308             r.add(k[i], it[i]);
13309         }
13310         r.getKey = this.getKey;
13311         return r;
13312     }
13313 });
13314 /**
13315  * Returns the item associated with the passed key or index.
13316  * @method
13317  * @param {String/Number} key The key or index of the item.
13318  * @return {Object} The item associated with the passed key.
13319  */
13320 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13321  * Based on:
13322  * Ext JS Library 1.1.1
13323  * Copyright(c) 2006-2007, Ext JS, LLC.
13324  *
13325  * Originally Released Under LGPL - original licence link has changed is not relivant.
13326  *
13327  * Fork - LGPL
13328  * <script type="text/javascript">
13329  */
13330 /**
13331  * @class Roo.util.JSON
13332  * Modified version of Douglas Crockford"s json.js that doesn"t
13333  * mess with the Object prototype 
13334  * http://www.json.org/js.html
13335  * @singleton
13336  */
13337 Roo.util.JSON = new (function(){
13338     var useHasOwn = {}.hasOwnProperty ? true : false;
13339     
13340     // crashes Safari in some instances
13341     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13342     
13343     var pad = function(n) {
13344         return n < 10 ? "0" + n : n;
13345     };
13346     
13347     var m = {
13348         "\b": '\\b',
13349         "\t": '\\t',
13350         "\n": '\\n',
13351         "\f": '\\f',
13352         "\r": '\\r',
13353         '"' : '\\"',
13354         "\\": '\\\\'
13355     };
13356
13357     var encodeString = function(s){
13358         if (/["\\\x00-\x1f]/.test(s)) {
13359             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13360                 var c = m[b];
13361                 if(c){
13362                     return c;
13363                 }
13364                 c = b.charCodeAt();
13365                 return "\\u00" +
13366                     Math.floor(c / 16).toString(16) +
13367                     (c % 16).toString(16);
13368             }) + '"';
13369         }
13370         return '"' + s + '"';
13371     };
13372     
13373     var encodeArray = function(o){
13374         var a = ["["], b, i, l = o.length, v;
13375             for (i = 0; i < l; i += 1) {
13376                 v = o[i];
13377                 switch (typeof v) {
13378                     case "undefined":
13379                     case "function":
13380                     case "unknown":
13381                         break;
13382                     default:
13383                         if (b) {
13384                             a.push(',');
13385                         }
13386                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13387                         b = true;
13388                 }
13389             }
13390             a.push("]");
13391             return a.join("");
13392     };
13393     
13394     var encodeDate = function(o){
13395         return '"' + o.getFullYear() + "-" +
13396                 pad(o.getMonth() + 1) + "-" +
13397                 pad(o.getDate()) + "T" +
13398                 pad(o.getHours()) + ":" +
13399                 pad(o.getMinutes()) + ":" +
13400                 pad(o.getSeconds()) + '"';
13401     };
13402     
13403     /**
13404      * Encodes an Object, Array or other value
13405      * @param {Mixed} o The variable to encode
13406      * @return {String} The JSON string
13407      */
13408     this.encode = function(o)
13409     {
13410         // should this be extended to fully wrap stringify..
13411         
13412         if(typeof o == "undefined" || o === null){
13413             return "null";
13414         }else if(o instanceof Array){
13415             return encodeArray(o);
13416         }else if(o instanceof Date){
13417             return encodeDate(o);
13418         }else if(typeof o == "string"){
13419             return encodeString(o);
13420         }else if(typeof o == "number"){
13421             return isFinite(o) ? String(o) : "null";
13422         }else if(typeof o == "boolean"){
13423             return String(o);
13424         }else {
13425             var a = ["{"], b, i, v;
13426             for (i in o) {
13427                 if(!useHasOwn || o.hasOwnProperty(i)) {
13428                     v = o[i];
13429                     switch (typeof v) {
13430                     case "undefined":
13431                     case "function":
13432                     case "unknown":
13433                         break;
13434                     default:
13435                         if(b){
13436                             a.push(',');
13437                         }
13438                         a.push(this.encode(i), ":",
13439                                 v === null ? "null" : this.encode(v));
13440                         b = true;
13441                     }
13442                 }
13443             }
13444             a.push("}");
13445             return a.join("");
13446         }
13447     };
13448     
13449     /**
13450      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13451      * @param {String} json The JSON string
13452      * @return {Object} The resulting object
13453      */
13454     this.decode = function(json){
13455         
13456         return  /** eval:var:json */ eval("(" + json + ')');
13457     };
13458 })();
13459 /** 
13460  * Shorthand for {@link Roo.util.JSON#encode}
13461  * @member Roo encode 
13462  * @method */
13463 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13464 /** 
13465  * Shorthand for {@link Roo.util.JSON#decode}
13466  * @member Roo decode 
13467  * @method */
13468 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13469 /*
13470  * Based on:
13471  * Ext JS Library 1.1.1
13472  * Copyright(c) 2006-2007, Ext JS, LLC.
13473  *
13474  * Originally Released Under LGPL - original licence link has changed is not relivant.
13475  *
13476  * Fork - LGPL
13477  * <script type="text/javascript">
13478  */
13479  
13480 /**
13481  * @class Roo.util.Format
13482  * Reusable data formatting functions
13483  * @singleton
13484  */
13485 Roo.util.Format = function(){
13486     var trimRe = /^\s+|\s+$/g;
13487     return {
13488         /**
13489          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13490          * @param {String} value The string to truncate
13491          * @param {Number} length The maximum length to allow before truncating
13492          * @return {String} The converted text
13493          */
13494         ellipsis : function(value, len){
13495             if(value && value.length > len){
13496                 return value.substr(0, len-3)+"...";
13497             }
13498             return value;
13499         },
13500
13501         /**
13502          * Checks a reference and converts it to empty string if it is undefined
13503          * @param {Mixed} value Reference to check
13504          * @return {Mixed} Empty string if converted, otherwise the original value
13505          */
13506         undef : function(value){
13507             return typeof value != "undefined" ? value : "";
13508         },
13509
13510         /**
13511          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13512          * @param {String} value The string to encode
13513          * @return {String} The encoded text
13514          */
13515         htmlEncode : function(value){
13516             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13517         },
13518
13519         /**
13520          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13521          * @param {String} value The string to decode
13522          * @return {String} The decoded text
13523          */
13524         htmlDecode : function(value){
13525             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13526         },
13527
13528         /**
13529          * Trims any whitespace from either side of a string
13530          * @param {String} value The text to trim
13531          * @return {String} The trimmed text
13532          */
13533         trim : function(value){
13534             return String(value).replace(trimRe, "");
13535         },
13536
13537         /**
13538          * Returns a substring from within an original string
13539          * @param {String} value The original text
13540          * @param {Number} start The start index of the substring
13541          * @param {Number} length The length of the substring
13542          * @return {String} The substring
13543          */
13544         substr : function(value, start, length){
13545             return String(value).substr(start, length);
13546         },
13547
13548         /**
13549          * Converts a string to all lower case letters
13550          * @param {String} value The text to convert
13551          * @return {String} The converted text
13552          */
13553         lowercase : function(value){
13554             return String(value).toLowerCase();
13555         },
13556
13557         /**
13558          * Converts a string to all upper case letters
13559          * @param {String} value The text to convert
13560          * @return {String} The converted text
13561          */
13562         uppercase : function(value){
13563             return String(value).toUpperCase();
13564         },
13565
13566         /**
13567          * Converts the first character only of a string to upper case
13568          * @param {String} value The text to convert
13569          * @return {String} The converted text
13570          */
13571         capitalize : function(value){
13572             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13573         },
13574
13575         // private
13576         call : function(value, fn){
13577             if(arguments.length > 2){
13578                 var args = Array.prototype.slice.call(arguments, 2);
13579                 args.unshift(value);
13580                  
13581                 return /** eval:var:value */  eval(fn).apply(window, args);
13582             }else{
13583                 /** eval:var:value */
13584                 return /** eval:var:value */ eval(fn).call(window, value);
13585             }
13586         },
13587
13588        
13589         /**
13590          * safer version of Math.toFixed..??/
13591          * @param {Number/String} value The numeric value to format
13592          * @param {Number/String} value Decimal places 
13593          * @return {String} The formatted currency string
13594          */
13595         toFixed : function(v, n)
13596         {
13597             // why not use to fixed - precision is buggered???
13598             if (!n) {
13599                 return Math.round(v-0);
13600             }
13601             var fact = Math.pow(10,n+1);
13602             v = (Math.round((v-0)*fact))/fact;
13603             var z = (''+fact).substring(2);
13604             if (v == Math.floor(v)) {
13605                 return Math.floor(v) + '.' + z;
13606             }
13607             
13608             // now just padd decimals..
13609             var ps = String(v).split('.');
13610             var fd = (ps[1] + z);
13611             var r = fd.substring(0,n); 
13612             var rm = fd.substring(n); 
13613             if (rm < 5) {
13614                 return ps[0] + '.' + r;
13615             }
13616             r*=1; // turn it into a number;
13617             r++;
13618             if (String(r).length != n) {
13619                 ps[0]*=1;
13620                 ps[0]++;
13621                 r = String(r).substring(1); // chop the end off.
13622             }
13623             
13624             return ps[0] + '.' + r;
13625              
13626         },
13627         
13628         /**
13629          * Format a number as US currency
13630          * @param {Number/String} value The numeric value to format
13631          * @return {String} The formatted currency string
13632          */
13633         usMoney : function(v){
13634             return '$' + Roo.util.Format.number(v);
13635         },
13636         
13637         /**
13638          * Format a number
13639          * eventually this should probably emulate php's number_format
13640          * @param {Number/String} value The numeric value to format
13641          * @param {Number} decimals number of decimal places
13642          * @return {String} The formatted currency string
13643          */
13644         number : function(v,decimals)
13645         {
13646             // multiply and round.
13647             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13648             var mul = Math.pow(10, decimals);
13649             var zero = String(mul).substring(1);
13650             v = (Math.round((v-0)*mul))/mul;
13651             
13652             // if it's '0' number.. then
13653             
13654             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13655             v = String(v);
13656             var ps = v.split('.');
13657             var whole = ps[0];
13658             
13659             
13660             var r = /(\d+)(\d{3})/;
13661             // add comma's
13662             while (r.test(whole)) {
13663                 whole = whole.replace(r, '$1' + ',' + '$2');
13664             }
13665             
13666             
13667             var sub = ps[1] ?
13668                     // has decimals..
13669                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13670                     // does not have decimals
13671                     (decimals ? ('.' + zero) : '');
13672             
13673             
13674             return whole + sub ;
13675         },
13676         
13677         /**
13678          * Parse a value into a formatted date using the specified format pattern.
13679          * @param {Mixed} value The value to format
13680          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13681          * @return {String} The formatted date string
13682          */
13683         date : function(v, format){
13684             if(!v){
13685                 return "";
13686             }
13687             if(!(v instanceof Date)){
13688                 v = new Date(Date.parse(v));
13689             }
13690             return v.dateFormat(format || Roo.util.Format.defaults.date);
13691         },
13692
13693         /**
13694          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13695          * @param {String} format Any valid date format string
13696          * @return {Function} The date formatting function
13697          */
13698         dateRenderer : function(format){
13699             return function(v){
13700                 return Roo.util.Format.date(v, format);  
13701             };
13702         },
13703
13704         // private
13705         stripTagsRE : /<\/?[^>]+>/gi,
13706         
13707         /**
13708          * Strips all HTML tags
13709          * @param {Mixed} value The text from which to strip tags
13710          * @return {String} The stripped text
13711          */
13712         stripTags : function(v){
13713             return !v ? v : String(v).replace(this.stripTagsRE, "");
13714         }
13715     };
13716 }();
13717 Roo.util.Format.defaults = {
13718     date : 'd/M/Y'
13719 };/*
13720  * Based on:
13721  * Ext JS Library 1.1.1
13722  * Copyright(c) 2006-2007, Ext JS, LLC.
13723  *
13724  * Originally Released Under LGPL - original licence link has changed is not relivant.
13725  *
13726  * Fork - LGPL
13727  * <script type="text/javascript">
13728  */
13729
13730
13731  
13732
13733 /**
13734  * @class Roo.MasterTemplate
13735  * @extends Roo.Template
13736  * Provides a template that can have child templates. The syntax is:
13737 <pre><code>
13738 var t = new Roo.MasterTemplate(
13739         '&lt;select name="{name}"&gt;',
13740                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13741         '&lt;/select&gt;'
13742 );
13743 t.add('options', {value: 'foo', text: 'bar'});
13744 // or you can add multiple child elements in one shot
13745 t.addAll('options', [
13746     {value: 'foo', text: 'bar'},
13747     {value: 'foo2', text: 'bar2'},
13748     {value: 'foo3', text: 'bar3'}
13749 ]);
13750 // then append, applying the master template values
13751 t.append('my-form', {name: 'my-select'});
13752 </code></pre>
13753 * A name attribute for the child template is not required if you have only one child
13754 * template or you want to refer to them by index.
13755  */
13756 Roo.MasterTemplate = function(){
13757     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13758     this.originalHtml = this.html;
13759     var st = {};
13760     var m, re = this.subTemplateRe;
13761     re.lastIndex = 0;
13762     var subIndex = 0;
13763     while(m = re.exec(this.html)){
13764         var name = m[1], content = m[2];
13765         st[subIndex] = {
13766             name: name,
13767             index: subIndex,
13768             buffer: [],
13769             tpl : new Roo.Template(content)
13770         };
13771         if(name){
13772             st[name] = st[subIndex];
13773         }
13774         st[subIndex].tpl.compile();
13775         st[subIndex].tpl.call = this.call.createDelegate(this);
13776         subIndex++;
13777     }
13778     this.subCount = subIndex;
13779     this.subs = st;
13780 };
13781 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13782     /**
13783     * The regular expression used to match sub templates
13784     * @type RegExp
13785     * @property
13786     */
13787     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13788
13789     /**
13790      * Applies the passed values to a child template.
13791      * @param {String/Number} name (optional) The name or index of the child template
13792      * @param {Array/Object} values The values to be applied to the template
13793      * @return {MasterTemplate} this
13794      */
13795      add : function(name, values){
13796         if(arguments.length == 1){
13797             values = arguments[0];
13798             name = 0;
13799         }
13800         var s = this.subs[name];
13801         s.buffer[s.buffer.length] = s.tpl.apply(values);
13802         return this;
13803     },
13804
13805     /**
13806      * Applies all the passed values to a child template.
13807      * @param {String/Number} name (optional) The name or index of the child template
13808      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13809      * @param {Boolean} reset (optional) True to reset the template first
13810      * @return {MasterTemplate} this
13811      */
13812     fill : function(name, values, reset){
13813         var a = arguments;
13814         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13815             values = a[0];
13816             name = 0;
13817             reset = a[1];
13818         }
13819         if(reset){
13820             this.reset();
13821         }
13822         for(var i = 0, len = values.length; i < len; i++){
13823             this.add(name, values[i]);
13824         }
13825         return this;
13826     },
13827
13828     /**
13829      * Resets the template for reuse
13830      * @return {MasterTemplate} this
13831      */
13832      reset : function(){
13833         var s = this.subs;
13834         for(var i = 0; i < this.subCount; i++){
13835             s[i].buffer = [];
13836         }
13837         return this;
13838     },
13839
13840     applyTemplate : function(values){
13841         var s = this.subs;
13842         var replaceIndex = -1;
13843         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13844             return s[++replaceIndex].buffer.join("");
13845         });
13846         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13847     },
13848
13849     apply : function(){
13850         return this.applyTemplate.apply(this, arguments);
13851     },
13852
13853     compile : function(){return this;}
13854 });
13855
13856 /**
13857  * Alias for fill().
13858  * @method
13859  */
13860 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13861  /**
13862  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13863  * var tpl = Roo.MasterTemplate.from('element-id');
13864  * @param {String/HTMLElement} el
13865  * @param {Object} config
13866  * @static
13867  */
13868 Roo.MasterTemplate.from = function(el, config){
13869     el = Roo.getDom(el);
13870     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13871 };/*
13872  * Based on:
13873  * Ext JS Library 1.1.1
13874  * Copyright(c) 2006-2007, Ext JS, LLC.
13875  *
13876  * Originally Released Under LGPL - original licence link has changed is not relivant.
13877  *
13878  * Fork - LGPL
13879  * <script type="text/javascript">
13880  */
13881
13882  
13883 /**
13884  * @class Roo.util.CSS
13885  * Utility class for manipulating CSS rules
13886  * @singleton
13887  */
13888 Roo.util.CSS = function(){
13889         var rules = null;
13890         var doc = document;
13891
13892     var camelRe = /(-[a-z])/gi;
13893     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13894
13895    return {
13896    /**
13897     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13898     * tag and appended to the HEAD of the document.
13899     * @param {String|Object} cssText The text containing the css rules
13900     * @param {String} id An id to add to the stylesheet for later removal
13901     * @return {StyleSheet}
13902     */
13903     createStyleSheet : function(cssText, id){
13904         var ss;
13905         var head = doc.getElementsByTagName("head")[0];
13906         var nrules = doc.createElement("style");
13907         nrules.setAttribute("type", "text/css");
13908         if(id){
13909             nrules.setAttribute("id", id);
13910         }
13911         if (typeof(cssText) != 'string') {
13912             // support object maps..
13913             // not sure if this a good idea.. 
13914             // perhaps it should be merged with the general css handling
13915             // and handle js style props.
13916             var cssTextNew = [];
13917             for(var n in cssText) {
13918                 var citems = [];
13919                 for(var k in cssText[n]) {
13920                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13921                 }
13922                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13923                 
13924             }
13925             cssText = cssTextNew.join("\n");
13926             
13927         }
13928        
13929        
13930        if(Roo.isIE){
13931            head.appendChild(nrules);
13932            ss = nrules.styleSheet;
13933            ss.cssText = cssText;
13934        }else{
13935            try{
13936                 nrules.appendChild(doc.createTextNode(cssText));
13937            }catch(e){
13938                nrules.cssText = cssText; 
13939            }
13940            head.appendChild(nrules);
13941            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13942        }
13943        this.cacheStyleSheet(ss);
13944        return ss;
13945    },
13946
13947    /**
13948     * Removes a style or link tag by id
13949     * @param {String} id The id of the tag
13950     */
13951    removeStyleSheet : function(id){
13952        var existing = doc.getElementById(id);
13953        if(existing){
13954            existing.parentNode.removeChild(existing);
13955        }
13956    },
13957
13958    /**
13959     * Dynamically swaps an existing stylesheet reference for a new one
13960     * @param {String} id The id of an existing link tag to remove
13961     * @param {String} url The href of the new stylesheet to include
13962     */
13963    swapStyleSheet : function(id, url){
13964        this.removeStyleSheet(id);
13965        var ss = doc.createElement("link");
13966        ss.setAttribute("rel", "stylesheet");
13967        ss.setAttribute("type", "text/css");
13968        ss.setAttribute("id", id);
13969        ss.setAttribute("href", url);
13970        doc.getElementsByTagName("head")[0].appendChild(ss);
13971    },
13972    
13973    /**
13974     * Refresh the rule cache if you have dynamically added stylesheets
13975     * @return {Object} An object (hash) of rules indexed by selector
13976     */
13977    refreshCache : function(){
13978        return this.getRules(true);
13979    },
13980
13981    // private
13982    cacheStyleSheet : function(stylesheet){
13983        if(!rules){
13984            rules = {};
13985        }
13986        try{// try catch for cross domain access issue
13987            var ssRules = stylesheet.cssRules || stylesheet.rules;
13988            for(var j = ssRules.length-1; j >= 0; --j){
13989                rules[ssRules[j].selectorText] = ssRules[j];
13990            }
13991        }catch(e){}
13992    },
13993    
13994    /**
13995     * Gets all css rules for the document
13996     * @param {Boolean} refreshCache true to refresh the internal cache
13997     * @return {Object} An object (hash) of rules indexed by selector
13998     */
13999    getRules : function(refreshCache){
14000                 if(rules == null || refreshCache){
14001                         rules = {};
14002                         var ds = doc.styleSheets;
14003                         for(var i =0, len = ds.length; i < len; i++){
14004                             try{
14005                         this.cacheStyleSheet(ds[i]);
14006                     }catch(e){} 
14007                 }
14008                 }
14009                 return rules;
14010         },
14011         
14012         /**
14013     * Gets an an individual CSS rule by selector(s)
14014     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14015     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14016     * @return {CSSRule} The CSS rule or null if one is not found
14017     */
14018    getRule : function(selector, refreshCache){
14019                 var rs = this.getRules(refreshCache);
14020                 if(!(selector instanceof Array)){
14021                     return rs[selector];
14022                 }
14023                 for(var i = 0; i < selector.length; i++){
14024                         if(rs[selector[i]]){
14025                                 return rs[selector[i]];
14026                         }
14027                 }
14028                 return null;
14029         },
14030         
14031         
14032         /**
14033     * Updates a rule property
14034     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14035     * @param {String} property The css property
14036     * @param {String} value The new value for the property
14037     * @return {Boolean} true If a rule was found and updated
14038     */
14039    updateRule : function(selector, property, value){
14040                 if(!(selector instanceof Array)){
14041                         var rule = this.getRule(selector);
14042                         if(rule){
14043                                 rule.style[property.replace(camelRe, camelFn)] = value;
14044                                 return true;
14045                         }
14046                 }else{
14047                         for(var i = 0; i < selector.length; i++){
14048                                 if(this.updateRule(selector[i], property, value)){
14049                                         return true;
14050                                 }
14051                         }
14052                 }
14053                 return false;
14054         }
14055    };   
14056 }();/*
14057  * Based on:
14058  * Ext JS Library 1.1.1
14059  * Copyright(c) 2006-2007, Ext JS, LLC.
14060  *
14061  * Originally Released Under LGPL - original licence link has changed is not relivant.
14062  *
14063  * Fork - LGPL
14064  * <script type="text/javascript">
14065  */
14066
14067  
14068
14069 /**
14070  * @class Roo.util.ClickRepeater
14071  * @extends Roo.util.Observable
14072  * 
14073  * A wrapper class which can be applied to any element. Fires a "click" event while the
14074  * mouse is pressed. The interval between firings may be specified in the config but
14075  * defaults to 10 milliseconds.
14076  * 
14077  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14078  * 
14079  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14080  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14081  * Similar to an autorepeat key delay.
14082  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14083  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14084  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14085  *           "interval" and "delay" are ignored. "immediate" is honored.
14086  * @cfg {Boolean} preventDefault True to prevent the default click event
14087  * @cfg {Boolean} stopDefault True to stop the default click event
14088  * 
14089  * @history
14090  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14091  *     2007-02-02 jvs Renamed to ClickRepeater
14092  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14093  *
14094  *  @constructor
14095  * @param {String/HTMLElement/Element} el The element to listen on
14096  * @param {Object} config
14097  **/
14098 Roo.util.ClickRepeater = function(el, config)
14099 {
14100     this.el = Roo.get(el);
14101     this.el.unselectable();
14102
14103     Roo.apply(this, config);
14104
14105     this.addEvents({
14106     /**
14107      * @event mousedown
14108      * Fires when the mouse button is depressed.
14109      * @param {Roo.util.ClickRepeater} this
14110      */
14111         "mousedown" : true,
14112     /**
14113      * @event click
14114      * Fires on a specified interval during the time the element is pressed.
14115      * @param {Roo.util.ClickRepeater} this
14116      */
14117         "click" : true,
14118     /**
14119      * @event mouseup
14120      * Fires when the mouse key is released.
14121      * @param {Roo.util.ClickRepeater} this
14122      */
14123         "mouseup" : true
14124     });
14125
14126     this.el.on("mousedown", this.handleMouseDown, this);
14127     if(this.preventDefault || this.stopDefault){
14128         this.el.on("click", function(e){
14129             if(this.preventDefault){
14130                 e.preventDefault();
14131             }
14132             if(this.stopDefault){
14133                 e.stopEvent();
14134             }
14135         }, this);
14136     }
14137
14138     // allow inline handler
14139     if(this.handler){
14140         this.on("click", this.handler,  this.scope || this);
14141     }
14142
14143     Roo.util.ClickRepeater.superclass.constructor.call(this);
14144 };
14145
14146 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14147     interval : 20,
14148     delay: 250,
14149     preventDefault : true,
14150     stopDefault : false,
14151     timer : 0,
14152
14153     // private
14154     handleMouseDown : function(){
14155         clearTimeout(this.timer);
14156         this.el.blur();
14157         if(this.pressClass){
14158             this.el.addClass(this.pressClass);
14159         }
14160         this.mousedownTime = new Date();
14161
14162         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14163         this.el.on("mouseout", this.handleMouseOut, this);
14164
14165         this.fireEvent("mousedown", this);
14166         this.fireEvent("click", this);
14167         
14168         this.timer = this.click.defer(this.delay || this.interval, this);
14169     },
14170
14171     // private
14172     click : function(){
14173         this.fireEvent("click", this);
14174         this.timer = this.click.defer(this.getInterval(), this);
14175     },
14176
14177     // private
14178     getInterval: function(){
14179         if(!this.accelerate){
14180             return this.interval;
14181         }
14182         var pressTime = this.mousedownTime.getElapsed();
14183         if(pressTime < 500){
14184             return 400;
14185         }else if(pressTime < 1700){
14186             return 320;
14187         }else if(pressTime < 2600){
14188             return 250;
14189         }else if(pressTime < 3500){
14190             return 180;
14191         }else if(pressTime < 4400){
14192             return 140;
14193         }else if(pressTime < 5300){
14194             return 80;
14195         }else if(pressTime < 6200){
14196             return 50;
14197         }else{
14198             return 10;
14199         }
14200     },
14201
14202     // private
14203     handleMouseOut : function(){
14204         clearTimeout(this.timer);
14205         if(this.pressClass){
14206             this.el.removeClass(this.pressClass);
14207         }
14208         this.el.on("mouseover", this.handleMouseReturn, this);
14209     },
14210
14211     // private
14212     handleMouseReturn : function(){
14213         this.el.un("mouseover", this.handleMouseReturn);
14214         if(this.pressClass){
14215             this.el.addClass(this.pressClass);
14216         }
14217         this.click();
14218     },
14219
14220     // private
14221     handleMouseUp : function(){
14222         clearTimeout(this.timer);
14223         this.el.un("mouseover", this.handleMouseReturn);
14224         this.el.un("mouseout", this.handleMouseOut);
14225         Roo.get(document).un("mouseup", this.handleMouseUp);
14226         this.el.removeClass(this.pressClass);
14227         this.fireEvent("mouseup", this);
14228     }
14229 });/*
14230  * Based on:
14231  * Ext JS Library 1.1.1
14232  * Copyright(c) 2006-2007, Ext JS, LLC.
14233  *
14234  * Originally Released Under LGPL - original licence link has changed is not relivant.
14235  *
14236  * Fork - LGPL
14237  * <script type="text/javascript">
14238  */
14239
14240  
14241 /**
14242  * @class Roo.KeyNav
14243  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14244  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14245  * way to implement custom navigation schemes for any UI component.</p>
14246  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14247  * pageUp, pageDown, del, home, end.  Usage:</p>
14248  <pre><code>
14249 var nav = new Roo.KeyNav("my-element", {
14250     "left" : function(e){
14251         this.moveLeft(e.ctrlKey);
14252     },
14253     "right" : function(e){
14254         this.moveRight(e.ctrlKey);
14255     },
14256     "enter" : function(e){
14257         this.save();
14258     },
14259     scope : this
14260 });
14261 </code></pre>
14262  * @constructor
14263  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14264  * @param {Object} config The config
14265  */
14266 Roo.KeyNav = function(el, config){
14267     this.el = Roo.get(el);
14268     Roo.apply(this, config);
14269     if(!this.disabled){
14270         this.disabled = true;
14271         this.enable();
14272     }
14273 };
14274
14275 Roo.KeyNav.prototype = {
14276     /**
14277      * @cfg {Boolean} disabled
14278      * True to disable this KeyNav instance (defaults to false)
14279      */
14280     disabled : false,
14281     /**
14282      * @cfg {String} defaultEventAction
14283      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14284      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14285      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14286      */
14287     defaultEventAction: "stopEvent",
14288     /**
14289      * @cfg {Boolean} forceKeyDown
14290      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14291      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14292      * handle keydown instead of keypress.
14293      */
14294     forceKeyDown : false,
14295
14296     // private
14297     prepareEvent : function(e){
14298         var k = e.getKey();
14299         var h = this.keyToHandler[k];
14300         //if(h && this[h]){
14301         //    e.stopPropagation();
14302         //}
14303         if(Roo.isSafari && h && k >= 37 && k <= 40){
14304             e.stopEvent();
14305         }
14306     },
14307
14308     // private
14309     relay : function(e){
14310         var k = e.getKey();
14311         var h = this.keyToHandler[k];
14312         if(h && this[h]){
14313             if(this.doRelay(e, this[h], h) !== true){
14314                 e[this.defaultEventAction]();
14315             }
14316         }
14317     },
14318
14319     // private
14320     doRelay : function(e, h, hname){
14321         return h.call(this.scope || this, e);
14322     },
14323
14324     // possible handlers
14325     enter : false,
14326     left : false,
14327     right : false,
14328     up : false,
14329     down : false,
14330     tab : false,
14331     esc : false,
14332     pageUp : false,
14333     pageDown : false,
14334     del : false,
14335     home : false,
14336     end : false,
14337
14338     // quick lookup hash
14339     keyToHandler : {
14340         37 : "left",
14341         39 : "right",
14342         38 : "up",
14343         40 : "down",
14344         33 : "pageUp",
14345         34 : "pageDown",
14346         46 : "del",
14347         36 : "home",
14348         35 : "end",
14349         13 : "enter",
14350         27 : "esc",
14351         9  : "tab"
14352     },
14353
14354         /**
14355          * Enable this KeyNav
14356          */
14357         enable: function(){
14358                 if(this.disabled){
14359             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14360             // the EventObject will normalize Safari automatically
14361             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14362                 this.el.on("keydown", this.relay,  this);
14363             }else{
14364                 this.el.on("keydown", this.prepareEvent,  this);
14365                 this.el.on("keypress", this.relay,  this);
14366             }
14367                     this.disabled = false;
14368                 }
14369         },
14370
14371         /**
14372          * Disable this KeyNav
14373          */
14374         disable: function(){
14375                 if(!this.disabled){
14376                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14377                 this.el.un("keydown", this.relay);
14378             }else{
14379                 this.el.un("keydown", this.prepareEvent);
14380                 this.el.un("keypress", this.relay);
14381             }
14382                     this.disabled = true;
14383                 }
14384         }
14385 };/*
14386  * Based on:
14387  * Ext JS Library 1.1.1
14388  * Copyright(c) 2006-2007, Ext JS, LLC.
14389  *
14390  * Originally Released Under LGPL - original licence link has changed is not relivant.
14391  *
14392  * Fork - LGPL
14393  * <script type="text/javascript">
14394  */
14395
14396  
14397 /**
14398  * @class Roo.KeyMap
14399  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14400  * The constructor accepts the same config object as defined by {@link #addBinding}.
14401  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14402  * combination it will call the function with this signature (if the match is a multi-key
14403  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14404  * A KeyMap can also handle a string representation of keys.<br />
14405  * Usage:
14406  <pre><code>
14407 // map one key by key code
14408 var map = new Roo.KeyMap("my-element", {
14409     key: 13, // or Roo.EventObject.ENTER
14410     fn: myHandler,
14411     scope: myObject
14412 });
14413
14414 // map multiple keys to one action by string
14415 var map = new Roo.KeyMap("my-element", {
14416     key: "a\r\n\t",
14417     fn: myHandler,
14418     scope: myObject
14419 });
14420
14421 // map multiple keys to multiple actions by strings and array of codes
14422 var map = new Roo.KeyMap("my-element", [
14423     {
14424         key: [10,13],
14425         fn: function(){ alert("Return was pressed"); }
14426     }, {
14427         key: "abc",
14428         fn: function(){ alert('a, b or c was pressed'); }
14429     }, {
14430         key: "\t",
14431         ctrl:true,
14432         shift:true,
14433         fn: function(){ alert('Control + shift + tab was pressed.'); }
14434     }
14435 ]);
14436 </code></pre>
14437  * <b>Note: A KeyMap starts enabled</b>
14438  * @constructor
14439  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14440  * @param {Object} config The config (see {@link #addBinding})
14441  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14442  */
14443 Roo.KeyMap = function(el, config, eventName){
14444     this.el  = Roo.get(el);
14445     this.eventName = eventName || "keydown";
14446     this.bindings = [];
14447     if(config){
14448         this.addBinding(config);
14449     }
14450     this.enable();
14451 };
14452
14453 Roo.KeyMap.prototype = {
14454     /**
14455      * True to stop the event from bubbling and prevent the default browser action if the
14456      * key was handled by the KeyMap (defaults to false)
14457      * @type Boolean
14458      */
14459     stopEvent : false,
14460
14461     /**
14462      * Add a new binding to this KeyMap. The following config object properties are supported:
14463      * <pre>
14464 Property    Type             Description
14465 ----------  ---------------  ----------------------------------------------------------------------
14466 key         String/Array     A single keycode or an array of keycodes to handle
14467 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14468 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14469 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14470 fn          Function         The function to call when KeyMap finds the expected key combination
14471 scope       Object           The scope of the callback function
14472 </pre>
14473      *
14474      * Usage:
14475      * <pre><code>
14476 // Create a KeyMap
14477 var map = new Roo.KeyMap(document, {
14478     key: Roo.EventObject.ENTER,
14479     fn: handleKey,
14480     scope: this
14481 });
14482
14483 //Add a new binding to the existing KeyMap later
14484 map.addBinding({
14485     key: 'abc',
14486     shift: true,
14487     fn: handleKey,
14488     scope: this
14489 });
14490 </code></pre>
14491      * @param {Object/Array} config A single KeyMap config or an array of configs
14492      */
14493         addBinding : function(config){
14494         if(config instanceof Array){
14495             for(var i = 0, len = config.length; i < len; i++){
14496                 this.addBinding(config[i]);
14497             }
14498             return;
14499         }
14500         var keyCode = config.key,
14501             shift = config.shift, 
14502             ctrl = config.ctrl, 
14503             alt = config.alt,
14504             fn = config.fn,
14505             scope = config.scope;
14506         if(typeof keyCode == "string"){
14507             var ks = [];
14508             var keyString = keyCode.toUpperCase();
14509             for(var j = 0, len = keyString.length; j < len; j++){
14510                 ks.push(keyString.charCodeAt(j));
14511             }
14512             keyCode = ks;
14513         }
14514         var keyArray = keyCode instanceof Array;
14515         var handler = function(e){
14516             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14517                 var k = e.getKey();
14518                 if(keyArray){
14519                     for(var i = 0, len = keyCode.length; i < len; i++){
14520                         if(keyCode[i] == k){
14521                           if(this.stopEvent){
14522                               e.stopEvent();
14523                           }
14524                           fn.call(scope || window, k, e);
14525                           return;
14526                         }
14527                     }
14528                 }else{
14529                     if(k == keyCode){
14530                         if(this.stopEvent){
14531                            e.stopEvent();
14532                         }
14533                         fn.call(scope || window, k, e);
14534                     }
14535                 }
14536             }
14537         };
14538         this.bindings.push(handler);  
14539         },
14540
14541     /**
14542      * Shorthand for adding a single key listener
14543      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14544      * following options:
14545      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14546      * @param {Function} fn The function to call
14547      * @param {Object} scope (optional) The scope of the function
14548      */
14549     on : function(key, fn, scope){
14550         var keyCode, shift, ctrl, alt;
14551         if(typeof key == "object" && !(key instanceof Array)){
14552             keyCode = key.key;
14553             shift = key.shift;
14554             ctrl = key.ctrl;
14555             alt = key.alt;
14556         }else{
14557             keyCode = key;
14558         }
14559         this.addBinding({
14560             key: keyCode,
14561             shift: shift,
14562             ctrl: ctrl,
14563             alt: alt,
14564             fn: fn,
14565             scope: scope
14566         })
14567     },
14568
14569     // private
14570     handleKeyDown : function(e){
14571             if(this.enabled){ //just in case
14572             var b = this.bindings;
14573             for(var i = 0, len = b.length; i < len; i++){
14574                 b[i].call(this, e);
14575             }
14576             }
14577         },
14578         
14579         /**
14580          * Returns true if this KeyMap is enabled
14581          * @return {Boolean} 
14582          */
14583         isEnabled : function(){
14584             return this.enabled;  
14585         },
14586         
14587         /**
14588          * Enables this KeyMap
14589          */
14590         enable: function(){
14591                 if(!this.enabled){
14592                     this.el.on(this.eventName, this.handleKeyDown, this);
14593                     this.enabled = true;
14594                 }
14595         },
14596
14597         /**
14598          * Disable this KeyMap
14599          */
14600         disable: function(){
14601                 if(this.enabled){
14602                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14603                     this.enabled = false;
14604                 }
14605         }
14606 };/*
14607  * Based on:
14608  * Ext JS Library 1.1.1
14609  * Copyright(c) 2006-2007, Ext JS, LLC.
14610  *
14611  * Originally Released Under LGPL - original licence link has changed is not relivant.
14612  *
14613  * Fork - LGPL
14614  * <script type="text/javascript">
14615  */
14616
14617  
14618 /**
14619  * @class Roo.util.TextMetrics
14620  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14621  * wide, in pixels, a given block of text will be.
14622  * @singleton
14623  */
14624 Roo.util.TextMetrics = function(){
14625     var shared;
14626     return {
14627         /**
14628          * Measures the size of the specified text
14629          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14630          * that can affect the size of the rendered text
14631          * @param {String} text The text to measure
14632          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14633          * in order to accurately measure the text height
14634          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14635          */
14636         measure : function(el, text, fixedWidth){
14637             if(!shared){
14638                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14639             }
14640             shared.bind(el);
14641             shared.setFixedWidth(fixedWidth || 'auto');
14642             return shared.getSize(text);
14643         },
14644
14645         /**
14646          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14647          * the overhead of multiple calls to initialize the style properties on each measurement.
14648          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14649          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14650          * in order to accurately measure the text height
14651          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14652          */
14653         createInstance : function(el, fixedWidth){
14654             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14655         }
14656     };
14657 }();
14658
14659  
14660
14661 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14662     var ml = new Roo.Element(document.createElement('div'));
14663     document.body.appendChild(ml.dom);
14664     ml.position('absolute');
14665     ml.setLeftTop(-1000, -1000);
14666     ml.hide();
14667
14668     if(fixedWidth){
14669         ml.setWidth(fixedWidth);
14670     }
14671      
14672     var instance = {
14673         /**
14674          * Returns the size of the specified text based on the internal element's style and width properties
14675          * @memberOf Roo.util.TextMetrics.Instance#
14676          * @param {String} text The text to measure
14677          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14678          */
14679         getSize : function(text){
14680             ml.update(text);
14681             var s = ml.getSize();
14682             ml.update('');
14683             return s;
14684         },
14685
14686         /**
14687          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14688          * that can affect the size of the rendered text
14689          * @memberOf Roo.util.TextMetrics.Instance#
14690          * @param {String/HTMLElement} el The element, dom node or id
14691          */
14692         bind : function(el){
14693             ml.setStyle(
14694                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14695             );
14696         },
14697
14698         /**
14699          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14700          * to set a fixed width in order to accurately measure the text height.
14701          * @memberOf Roo.util.TextMetrics.Instance#
14702          * @param {Number} width The width to set on the element
14703          */
14704         setFixedWidth : function(width){
14705             ml.setWidth(width);
14706         },
14707
14708         /**
14709          * Returns the measured width of the specified text
14710          * @memberOf Roo.util.TextMetrics.Instance#
14711          * @param {String} text The text to measure
14712          * @return {Number} width The width in pixels
14713          */
14714         getWidth : function(text){
14715             ml.dom.style.width = 'auto';
14716             return this.getSize(text).width;
14717         },
14718
14719         /**
14720          * Returns the measured height of the specified text.  For multiline text, be sure to call
14721          * {@link #setFixedWidth} if necessary.
14722          * @memberOf Roo.util.TextMetrics.Instance#
14723          * @param {String} text The text to measure
14724          * @return {Number} height The height in pixels
14725          */
14726         getHeight : function(text){
14727             return this.getSize(text).height;
14728         }
14729     };
14730
14731     instance.bind(bindTo);
14732
14733     return instance;
14734 };
14735
14736 // backwards compat
14737 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14738  * Based on:
14739  * Ext JS Library 1.1.1
14740  * Copyright(c) 2006-2007, Ext JS, LLC.
14741  *
14742  * Originally Released Under LGPL - original licence link has changed is not relivant.
14743  *
14744  * Fork - LGPL
14745  * <script type="text/javascript">
14746  */
14747
14748 /**
14749  * @class Roo.state.Provider
14750  * Abstract base class for state provider implementations. This class provides methods
14751  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14752  * Provider interface.
14753  */
14754 Roo.state.Provider = function(){
14755     /**
14756      * @event statechange
14757      * Fires when a state change occurs.
14758      * @param {Provider} this This state provider
14759      * @param {String} key The state key which was changed
14760      * @param {String} value The encoded value for the state
14761      */
14762     this.addEvents({
14763         "statechange": true
14764     });
14765     this.state = {};
14766     Roo.state.Provider.superclass.constructor.call(this);
14767 };
14768 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14769     /**
14770      * Returns the current value for a key
14771      * @param {String} name The key name
14772      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14773      * @return {Mixed} The state data
14774      */
14775     get : function(name, defaultValue){
14776         return typeof this.state[name] == "undefined" ?
14777             defaultValue : this.state[name];
14778     },
14779     
14780     /**
14781      * Clears a value from the state
14782      * @param {String} name The key name
14783      */
14784     clear : function(name){
14785         delete this.state[name];
14786         this.fireEvent("statechange", this, name, null);
14787     },
14788     
14789     /**
14790      * Sets the value for a key
14791      * @param {String} name The key name
14792      * @param {Mixed} value The value to set
14793      */
14794     set : function(name, value){
14795         this.state[name] = value;
14796         this.fireEvent("statechange", this, name, value);
14797     },
14798     
14799     /**
14800      * Decodes a string previously encoded with {@link #encodeValue}.
14801      * @param {String} value The value to decode
14802      * @return {Mixed} The decoded value
14803      */
14804     decodeValue : function(cookie){
14805         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14806         var matches = re.exec(unescape(cookie));
14807         if(!matches || !matches[1]) return; // non state cookie
14808         var type = matches[1];
14809         var v = matches[2];
14810         switch(type){
14811             case "n":
14812                 return parseFloat(v);
14813             case "d":
14814                 return new Date(Date.parse(v));
14815             case "b":
14816                 return (v == "1");
14817             case "a":
14818                 var all = [];
14819                 var values = v.split("^");
14820                 for(var i = 0, len = values.length; i < len; i++){
14821                     all.push(this.decodeValue(values[i]));
14822                 }
14823                 return all;
14824            case "o":
14825                 var all = {};
14826                 var values = v.split("^");
14827                 for(var i = 0, len = values.length; i < len; i++){
14828                     var kv = values[i].split("=");
14829                     all[kv[0]] = this.decodeValue(kv[1]);
14830                 }
14831                 return all;
14832            default:
14833                 return v;
14834         }
14835     },
14836     
14837     /**
14838      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14839      * @param {Mixed} value The value to encode
14840      * @return {String} The encoded value
14841      */
14842     encodeValue : function(v){
14843         var enc;
14844         if(typeof v == "number"){
14845             enc = "n:" + v;
14846         }else if(typeof v == "boolean"){
14847             enc = "b:" + (v ? "1" : "0");
14848         }else if(v instanceof Date){
14849             enc = "d:" + v.toGMTString();
14850         }else if(v instanceof Array){
14851             var flat = "";
14852             for(var i = 0, len = v.length; i < len; i++){
14853                 flat += this.encodeValue(v[i]);
14854                 if(i != len-1) flat += "^";
14855             }
14856             enc = "a:" + flat;
14857         }else if(typeof v == "object"){
14858             var flat = "";
14859             for(var key in v){
14860                 if(typeof v[key] != "function"){
14861                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14862                 }
14863             }
14864             enc = "o:" + flat.substring(0, flat.length-1);
14865         }else{
14866             enc = "s:" + v;
14867         }
14868         return escape(enc);        
14869     }
14870 });
14871
14872 /*
14873  * Based on:
14874  * Ext JS Library 1.1.1
14875  * Copyright(c) 2006-2007, Ext JS, LLC.
14876  *
14877  * Originally Released Under LGPL - original licence link has changed is not relivant.
14878  *
14879  * Fork - LGPL
14880  * <script type="text/javascript">
14881  */
14882 /**
14883  * @class Roo.state.Manager
14884  * This is the global state manager. By default all components that are "state aware" check this class
14885  * for state information if you don't pass them a custom state provider. In order for this class
14886  * to be useful, it must be initialized with a provider when your application initializes.
14887  <pre><code>
14888 // in your initialization function
14889 init : function(){
14890    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14891    ...
14892    // supposed you have a {@link Roo.BorderLayout}
14893    var layout = new Roo.BorderLayout(...);
14894    layout.restoreState();
14895    // or a {Roo.BasicDialog}
14896    var dialog = new Roo.BasicDialog(...);
14897    dialog.restoreState();
14898  </code></pre>
14899  * @singleton
14900  */
14901 Roo.state.Manager = function(){
14902     var provider = new Roo.state.Provider();
14903     
14904     return {
14905         /**
14906          * Configures the default state provider for your application
14907          * @param {Provider} stateProvider The state provider to set
14908          */
14909         setProvider : function(stateProvider){
14910             provider = stateProvider;
14911         },
14912         
14913         /**
14914          * Returns the current value for a key
14915          * @param {String} name The key name
14916          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14917          * @return {Mixed} The state data
14918          */
14919         get : function(key, defaultValue){
14920             return provider.get(key, defaultValue);
14921         },
14922         
14923         /**
14924          * Sets the value for a key
14925          * @param {String} name The key name
14926          * @param {Mixed} value The state data
14927          */
14928          set : function(key, value){
14929             provider.set(key, value);
14930         },
14931         
14932         /**
14933          * Clears a value from the state
14934          * @param {String} name The key name
14935          */
14936         clear : function(key){
14937             provider.clear(key);
14938         },
14939         
14940         /**
14941          * Gets the currently configured state provider
14942          * @return {Provider} The state provider
14943          */
14944         getProvider : function(){
14945             return provider;
14946         }
14947     };
14948 }();
14949 /*
14950  * Based on:
14951  * Ext JS Library 1.1.1
14952  * Copyright(c) 2006-2007, Ext JS, LLC.
14953  *
14954  * Originally Released Under LGPL - original licence link has changed is not relivant.
14955  *
14956  * Fork - LGPL
14957  * <script type="text/javascript">
14958  */
14959 /**
14960  * @class Roo.state.CookieProvider
14961  * @extends Roo.state.Provider
14962  * The default Provider implementation which saves state via cookies.
14963  * <br />Usage:
14964  <pre><code>
14965    var cp = new Roo.state.CookieProvider({
14966        path: "/cgi-bin/",
14967        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14968        domain: "roojs.com"
14969    })
14970    Roo.state.Manager.setProvider(cp);
14971  </code></pre>
14972  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14973  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14974  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14975  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14976  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14977  * domain the page is running on including the 'www' like 'www.roojs.com')
14978  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14979  * @constructor
14980  * Create a new CookieProvider
14981  * @param {Object} config The configuration object
14982  */
14983 Roo.state.CookieProvider = function(config){
14984     Roo.state.CookieProvider.superclass.constructor.call(this);
14985     this.path = "/";
14986     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14987     this.domain = null;
14988     this.secure = false;
14989     Roo.apply(this, config);
14990     this.state = this.readCookies();
14991 };
14992
14993 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14994     // private
14995     set : function(name, value){
14996         if(typeof value == "undefined" || value === null){
14997             this.clear(name);
14998             return;
14999         }
15000         this.setCookie(name, value);
15001         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15002     },
15003
15004     // private
15005     clear : function(name){
15006         this.clearCookie(name);
15007         Roo.state.CookieProvider.superclass.clear.call(this, name);
15008     },
15009
15010     // private
15011     readCookies : function(){
15012         var cookies = {};
15013         var c = document.cookie + ";";
15014         var re = /\s?(.*?)=(.*?);/g;
15015         var matches;
15016         while((matches = re.exec(c)) != null){
15017             var name = matches[1];
15018             var value = matches[2];
15019             if(name && name.substring(0,3) == "ys-"){
15020                 cookies[name.substr(3)] = this.decodeValue(value);
15021             }
15022         }
15023         return cookies;
15024     },
15025
15026     // private
15027     setCookie : function(name, value){
15028         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15029            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15030            ((this.path == null) ? "" : ("; path=" + this.path)) +
15031            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15032            ((this.secure == true) ? "; secure" : "");
15033     },
15034
15035     // private
15036     clearCookie : function(name){
15037         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15038            ((this.path == null) ? "" : ("; path=" + this.path)) +
15039            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15040            ((this.secure == true) ? "; secure" : "");
15041     }
15042 });/*
15043  * Based on:
15044  * Ext JS Library 1.1.1
15045  * Copyright(c) 2006-2007, Ext JS, LLC.
15046  *
15047  * Originally Released Under LGPL - original licence link has changed is not relivant.
15048  *
15049  * Fork - LGPL
15050  * <script type="text/javascript">
15051  */
15052  
15053
15054 /**
15055  * @class Roo.ComponentMgr
15056  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15057  * @singleton
15058  */
15059 Roo.ComponentMgr = function(){
15060     var all = new Roo.util.MixedCollection();
15061
15062     return {
15063         /**
15064          * Registers a component.
15065          * @param {Roo.Component} c The component
15066          */
15067         register : function(c){
15068             all.add(c);
15069         },
15070
15071         /**
15072          * Unregisters a component.
15073          * @param {Roo.Component} c The component
15074          */
15075         unregister : function(c){
15076             all.remove(c);
15077         },
15078
15079         /**
15080          * Returns a component by id
15081          * @param {String} id The component id
15082          */
15083         get : function(id){
15084             return all.get(id);
15085         },
15086
15087         /**
15088          * Registers a function that will be called when a specified component is added to ComponentMgr
15089          * @param {String} id The component id
15090          * @param {Funtction} fn The callback function
15091          * @param {Object} scope The scope of the callback
15092          */
15093         onAvailable : function(id, fn, scope){
15094             all.on("add", function(index, o){
15095                 if(o.id == id){
15096                     fn.call(scope || o, o);
15097                     all.un("add", fn, scope);
15098                 }
15099             });
15100         }
15101     };
15102 }();/*
15103  * Based on:
15104  * Ext JS Library 1.1.1
15105  * Copyright(c) 2006-2007, Ext JS, LLC.
15106  *
15107  * Originally Released Under LGPL - original licence link has changed is not relivant.
15108  *
15109  * Fork - LGPL
15110  * <script type="text/javascript">
15111  */
15112  
15113 /**
15114  * @class Roo.Component
15115  * @extends Roo.util.Observable
15116  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15117  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15118  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15119  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15120  * All visual components (widgets) that require rendering into a layout should subclass Component.
15121  * @constructor
15122  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15123  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
15124  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15125  */
15126 Roo.Component = function(config){
15127     config = config || {};
15128     if(config.tagName || config.dom || typeof config == "string"){ // element object
15129         config = {el: config, id: config.id || config};
15130     }
15131     this.initialConfig = config;
15132
15133     Roo.apply(this, config);
15134     this.addEvents({
15135         /**
15136          * @event disable
15137          * Fires after the component is disabled.
15138              * @param {Roo.Component} this
15139              */
15140         disable : true,
15141         /**
15142          * @event enable
15143          * Fires after the component is enabled.
15144              * @param {Roo.Component} this
15145              */
15146         enable : true,
15147         /**
15148          * @event beforeshow
15149          * Fires before the component is shown.  Return false to stop the show.
15150              * @param {Roo.Component} this
15151              */
15152         beforeshow : true,
15153         /**
15154          * @event show
15155          * Fires after the component is shown.
15156              * @param {Roo.Component} this
15157              */
15158         show : true,
15159         /**
15160          * @event beforehide
15161          * Fires before the component is hidden. Return false to stop the hide.
15162              * @param {Roo.Component} this
15163              */
15164         beforehide : true,
15165         /**
15166          * @event hide
15167          * Fires after the component is hidden.
15168              * @param {Roo.Component} this
15169              */
15170         hide : true,
15171         /**
15172          * @event beforerender
15173          * Fires before the component is rendered. Return false to stop the render.
15174              * @param {Roo.Component} this
15175              */
15176         beforerender : true,
15177         /**
15178          * @event render
15179          * Fires after the component is rendered.
15180              * @param {Roo.Component} this
15181              */
15182         render : true,
15183         /**
15184          * @event beforedestroy
15185          * Fires before the component is destroyed. Return false to stop the destroy.
15186              * @param {Roo.Component} this
15187              */
15188         beforedestroy : true,
15189         /**
15190          * @event destroy
15191          * Fires after the component is destroyed.
15192              * @param {Roo.Component} this
15193              */
15194         destroy : true
15195     });
15196     if(!this.id){
15197         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15198     }
15199     Roo.ComponentMgr.register(this);
15200     Roo.Component.superclass.constructor.call(this);
15201     this.initComponent();
15202     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15203         this.render(this.renderTo);
15204         delete this.renderTo;
15205     }
15206 };
15207
15208 /** @private */
15209 Roo.Component.AUTO_ID = 1000;
15210
15211 Roo.extend(Roo.Component, Roo.util.Observable, {
15212     /**
15213      * @scope Roo.Component.prototype
15214      * @type {Boolean}
15215      * true if this component is hidden. Read-only.
15216      */
15217     hidden : false,
15218     /**
15219      * @type {Boolean}
15220      * true if this component is disabled. Read-only.
15221      */
15222     disabled : false,
15223     /**
15224      * @type {Boolean}
15225      * true if this component has been rendered. Read-only.
15226      */
15227     rendered : false,
15228     
15229     /** @cfg {String} disableClass
15230      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15231      */
15232     disabledClass : "x-item-disabled",
15233         /** @cfg {Boolean} allowDomMove
15234          * Whether the component can move the Dom node when rendering (defaults to true).
15235          */
15236     allowDomMove : true,
15237     /** @cfg {String} hideMode (display|visibility)
15238      * How this component should hidden. Supported values are
15239      * "visibility" (css visibility), "offsets" (negative offset position) and
15240      * "display" (css display) - defaults to "display".
15241      */
15242     hideMode: 'display',
15243
15244     /** @private */
15245     ctype : "Roo.Component",
15246
15247     /**
15248      * @cfg {String} actionMode 
15249      * which property holds the element that used for  hide() / show() / disable() / enable()
15250      * default is 'el' 
15251      */
15252     actionMode : "el",
15253
15254     /** @private */
15255     getActionEl : function(){
15256         return this[this.actionMode];
15257     },
15258
15259     initComponent : Roo.emptyFn,
15260     /**
15261      * If this is a lazy rendering component, render it to its container element.
15262      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
15263      */
15264     render : function(container, position){
15265         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15266             if(!container && this.el){
15267                 this.el = Roo.get(this.el);
15268                 container = this.el.dom.parentNode;
15269                 this.allowDomMove = false;
15270             }
15271             this.container = Roo.get(container);
15272             this.rendered = true;
15273             if(position !== undefined){
15274                 if(typeof position == 'number'){
15275                     position = this.container.dom.childNodes[position];
15276                 }else{
15277                     position = Roo.getDom(position);
15278                 }
15279             }
15280             this.onRender(this.container, position || null);
15281             if(this.cls){
15282                 this.el.addClass(this.cls);
15283                 delete this.cls;
15284             }
15285             if(this.style){
15286                 this.el.applyStyles(this.style);
15287                 delete this.style;
15288             }
15289             this.fireEvent("render", this);
15290             this.afterRender(this.container);
15291             if(this.hidden){
15292                 this.hide();
15293             }
15294             if(this.disabled){
15295                 this.disable();
15296             }
15297         }
15298         return this;
15299     },
15300
15301     /** @private */
15302     // default function is not really useful
15303     onRender : function(ct, position){
15304         if(this.el){
15305             this.el = Roo.get(this.el);
15306             if(this.allowDomMove !== false){
15307                 ct.dom.insertBefore(this.el.dom, position);
15308             }
15309         }
15310     },
15311
15312     /** @private */
15313     getAutoCreate : function(){
15314         var cfg = typeof this.autoCreate == "object" ?
15315                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15316         if(this.id && !cfg.id){
15317             cfg.id = this.id;
15318         }
15319         return cfg;
15320     },
15321
15322     /** @private */
15323     afterRender : Roo.emptyFn,
15324
15325     /**
15326      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15327      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15328      */
15329     destroy : function(){
15330         if(this.fireEvent("beforedestroy", this) !== false){
15331             this.purgeListeners();
15332             this.beforeDestroy();
15333             if(this.rendered){
15334                 this.el.removeAllListeners();
15335                 this.el.remove();
15336                 if(this.actionMode == "container"){
15337                     this.container.remove();
15338                 }
15339             }
15340             this.onDestroy();
15341             Roo.ComponentMgr.unregister(this);
15342             this.fireEvent("destroy", this);
15343         }
15344     },
15345
15346         /** @private */
15347     beforeDestroy : function(){
15348
15349     },
15350
15351         /** @private */
15352         onDestroy : function(){
15353
15354     },
15355
15356     /**
15357      * Returns the underlying {@link Roo.Element}.
15358      * @return {Roo.Element} The element
15359      */
15360     getEl : function(){
15361         return this.el;
15362     },
15363
15364     /**
15365      * Returns the id of this component.
15366      * @return {String}
15367      */
15368     getId : function(){
15369         return this.id;
15370     },
15371
15372     /**
15373      * Try to focus this component.
15374      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15375      * @return {Roo.Component} this
15376      */
15377     focus : function(selectText){
15378         if(this.rendered){
15379             this.el.focus();
15380             if(selectText === true){
15381                 this.el.dom.select();
15382             }
15383         }
15384         return this;
15385     },
15386
15387     /** @private */
15388     blur : function(){
15389         if(this.rendered){
15390             this.el.blur();
15391         }
15392         return this;
15393     },
15394
15395     /**
15396      * Disable this component.
15397      * @return {Roo.Component} this
15398      */
15399     disable : function(){
15400         if(this.rendered){
15401             this.onDisable();
15402         }
15403         this.disabled = true;
15404         this.fireEvent("disable", this);
15405         return this;
15406     },
15407
15408         // private
15409     onDisable : function(){
15410         this.getActionEl().addClass(this.disabledClass);
15411         this.el.dom.disabled = true;
15412     },
15413
15414     /**
15415      * Enable this component.
15416      * @return {Roo.Component} this
15417      */
15418     enable : function(){
15419         if(this.rendered){
15420             this.onEnable();
15421         }
15422         this.disabled = false;
15423         this.fireEvent("enable", this);
15424         return this;
15425     },
15426
15427         // private
15428     onEnable : function(){
15429         this.getActionEl().removeClass(this.disabledClass);
15430         this.el.dom.disabled = false;
15431     },
15432
15433     /**
15434      * Convenience function for setting disabled/enabled by boolean.
15435      * @param {Boolean} disabled
15436      */
15437     setDisabled : function(disabled){
15438         this[disabled ? "disable" : "enable"]();
15439     },
15440
15441     /**
15442      * Show this component.
15443      * @return {Roo.Component} this
15444      */
15445     show: function(){
15446         if(this.fireEvent("beforeshow", this) !== false){
15447             this.hidden = false;
15448             if(this.rendered){
15449                 this.onShow();
15450             }
15451             this.fireEvent("show", this);
15452         }
15453         return this;
15454     },
15455
15456     // private
15457     onShow : function(){
15458         var ae = this.getActionEl();
15459         if(this.hideMode == 'visibility'){
15460             ae.dom.style.visibility = "visible";
15461         }else if(this.hideMode == 'offsets'){
15462             ae.removeClass('x-hidden');
15463         }else{
15464             ae.dom.style.display = "";
15465         }
15466     },
15467
15468     /**
15469      * Hide this component.
15470      * @return {Roo.Component} this
15471      */
15472     hide: function(){
15473         if(this.fireEvent("beforehide", this) !== false){
15474             this.hidden = true;
15475             if(this.rendered){
15476                 this.onHide();
15477             }
15478             this.fireEvent("hide", this);
15479         }
15480         return this;
15481     },
15482
15483     // private
15484     onHide : function(){
15485         var ae = this.getActionEl();
15486         if(this.hideMode == 'visibility'){
15487             ae.dom.style.visibility = "hidden";
15488         }else if(this.hideMode == 'offsets'){
15489             ae.addClass('x-hidden');
15490         }else{
15491             ae.dom.style.display = "none";
15492         }
15493     },
15494
15495     /**
15496      * Convenience function to hide or show this component by boolean.
15497      * @param {Boolean} visible True to show, false to hide
15498      * @return {Roo.Component} this
15499      */
15500     setVisible: function(visible){
15501         if(visible) {
15502             this.show();
15503         }else{
15504             this.hide();
15505         }
15506         return this;
15507     },
15508
15509     /**
15510      * Returns true if this component is visible.
15511      */
15512     isVisible : function(){
15513         return this.getActionEl().isVisible();
15514     },
15515
15516     cloneConfig : function(overrides){
15517         overrides = overrides || {};
15518         var id = overrides.id || Roo.id();
15519         var cfg = Roo.applyIf(overrides, this.initialConfig);
15520         cfg.id = id; // prevent dup id
15521         return new this.constructor(cfg);
15522     }
15523 });/*
15524  * Based on:
15525  * Ext JS Library 1.1.1
15526  * Copyright(c) 2006-2007, Ext JS, LLC.
15527  *
15528  * Originally Released Under LGPL - original licence link has changed is not relivant.
15529  *
15530  * Fork - LGPL
15531  * <script type="text/javascript">
15532  */
15533
15534 /**
15535  * @class Roo.BoxComponent
15536  * @extends Roo.Component
15537  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15538  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15539  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15540  * layout containers.
15541  * @constructor
15542  * @param {Roo.Element/String/Object} config The configuration options.
15543  */
15544 Roo.BoxComponent = function(config){
15545     Roo.Component.call(this, config);
15546     this.addEvents({
15547         /**
15548          * @event resize
15549          * Fires after the component is resized.
15550              * @param {Roo.Component} this
15551              * @param {Number} adjWidth The box-adjusted width that was set
15552              * @param {Number} adjHeight The box-adjusted height that was set
15553              * @param {Number} rawWidth The width that was originally specified
15554              * @param {Number} rawHeight The height that was originally specified
15555              */
15556         resize : true,
15557         /**
15558          * @event move
15559          * Fires after the component is moved.
15560              * @param {Roo.Component} this
15561              * @param {Number} x The new x position
15562              * @param {Number} y The new y position
15563              */
15564         move : true
15565     });
15566 };
15567
15568 Roo.extend(Roo.BoxComponent, Roo.Component, {
15569     // private, set in afterRender to signify that the component has been rendered
15570     boxReady : false,
15571     // private, used to defer height settings to subclasses
15572     deferHeight: false,
15573     /** @cfg {Number} width
15574      * width (optional) size of component
15575      */
15576      /** @cfg {Number} height
15577      * height (optional) size of component
15578      */
15579      
15580     /**
15581      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15582      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15583      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15584      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15585      * @return {Roo.BoxComponent} this
15586      */
15587     setSize : function(w, h){
15588         // support for standard size objects
15589         if(typeof w == 'object'){
15590             h = w.height;
15591             w = w.width;
15592         }
15593         // not rendered
15594         if(!this.boxReady){
15595             this.width = w;
15596             this.height = h;
15597             return this;
15598         }
15599
15600         // prevent recalcs when not needed
15601         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15602             return this;
15603         }
15604         this.lastSize = {width: w, height: h};
15605
15606         var adj = this.adjustSize(w, h);
15607         var aw = adj.width, ah = adj.height;
15608         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15609             var rz = this.getResizeEl();
15610             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15611                 rz.setSize(aw, ah);
15612             }else if(!this.deferHeight && ah !== undefined){
15613                 rz.setHeight(ah);
15614             }else if(aw !== undefined){
15615                 rz.setWidth(aw);
15616             }
15617             this.onResize(aw, ah, w, h);
15618             this.fireEvent('resize', this, aw, ah, w, h);
15619         }
15620         return this;
15621     },
15622
15623     /**
15624      * Gets the current size of the component's underlying element.
15625      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15626      */
15627     getSize : function(){
15628         return this.el.getSize();
15629     },
15630
15631     /**
15632      * Gets the current XY position of the component's underlying element.
15633      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15634      * @return {Array} The XY position of the element (e.g., [100, 200])
15635      */
15636     getPosition : function(local){
15637         if(local === true){
15638             return [this.el.getLeft(true), this.el.getTop(true)];
15639         }
15640         return this.xy || this.el.getXY();
15641     },
15642
15643     /**
15644      * Gets the current box measurements of the component's underlying element.
15645      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15646      * @returns {Object} box An object in the format {x, y, width, height}
15647      */
15648     getBox : function(local){
15649         var s = this.el.getSize();
15650         if(local){
15651             s.x = this.el.getLeft(true);
15652             s.y = this.el.getTop(true);
15653         }else{
15654             var xy = this.xy || this.el.getXY();
15655             s.x = xy[0];
15656             s.y = xy[1];
15657         }
15658         return s;
15659     },
15660
15661     /**
15662      * Sets the current box measurements of the component's underlying element.
15663      * @param {Object} box An object in the format {x, y, width, height}
15664      * @returns {Roo.BoxComponent} this
15665      */
15666     updateBox : function(box){
15667         this.setSize(box.width, box.height);
15668         this.setPagePosition(box.x, box.y);
15669         return this;
15670     },
15671
15672     // protected
15673     getResizeEl : function(){
15674         return this.resizeEl || this.el;
15675     },
15676
15677     // protected
15678     getPositionEl : function(){
15679         return this.positionEl || this.el;
15680     },
15681
15682     /**
15683      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15684      * This method fires the move event.
15685      * @param {Number} left The new left
15686      * @param {Number} top The new top
15687      * @returns {Roo.BoxComponent} this
15688      */
15689     setPosition : function(x, y){
15690         this.x = x;
15691         this.y = y;
15692         if(!this.boxReady){
15693             return this;
15694         }
15695         var adj = this.adjustPosition(x, y);
15696         var ax = adj.x, ay = adj.y;
15697
15698         var el = this.getPositionEl();
15699         if(ax !== undefined || ay !== undefined){
15700             if(ax !== undefined && ay !== undefined){
15701                 el.setLeftTop(ax, ay);
15702             }else if(ax !== undefined){
15703                 el.setLeft(ax);
15704             }else if(ay !== undefined){
15705                 el.setTop(ay);
15706             }
15707             this.onPosition(ax, ay);
15708             this.fireEvent('move', this, ax, ay);
15709         }
15710         return this;
15711     },
15712
15713     /**
15714      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15715      * This method fires the move event.
15716      * @param {Number} x The new x position
15717      * @param {Number} y The new y position
15718      * @returns {Roo.BoxComponent} this
15719      */
15720     setPagePosition : function(x, y){
15721         this.pageX = x;
15722         this.pageY = y;
15723         if(!this.boxReady){
15724             return;
15725         }
15726         if(x === undefined || y === undefined){ // cannot translate undefined points
15727             return;
15728         }
15729         var p = this.el.translatePoints(x, y);
15730         this.setPosition(p.left, p.top);
15731         return this;
15732     },
15733
15734     // private
15735     onRender : function(ct, position){
15736         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15737         if(this.resizeEl){
15738             this.resizeEl = Roo.get(this.resizeEl);
15739         }
15740         if(this.positionEl){
15741             this.positionEl = Roo.get(this.positionEl);
15742         }
15743     },
15744
15745     // private
15746     afterRender : function(){
15747         Roo.BoxComponent.superclass.afterRender.call(this);
15748         this.boxReady = true;
15749         this.setSize(this.width, this.height);
15750         if(this.x || this.y){
15751             this.setPosition(this.x, this.y);
15752         }
15753         if(this.pageX || this.pageY){
15754             this.setPagePosition(this.pageX, this.pageY);
15755         }
15756     },
15757
15758     /**
15759      * Force the component's size to recalculate based on the underlying element's current height and width.
15760      * @returns {Roo.BoxComponent} this
15761      */
15762     syncSize : function(){
15763         delete this.lastSize;
15764         this.setSize(this.el.getWidth(), this.el.getHeight());
15765         return this;
15766     },
15767
15768     /**
15769      * Called after the component is resized, this method is empty by default but can be implemented by any
15770      * subclass that needs to perform custom logic after a resize occurs.
15771      * @param {Number} adjWidth The box-adjusted width that was set
15772      * @param {Number} adjHeight The box-adjusted height that was set
15773      * @param {Number} rawWidth The width that was originally specified
15774      * @param {Number} rawHeight The height that was originally specified
15775      */
15776     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15777
15778     },
15779
15780     /**
15781      * Called after the component is moved, this method is empty by default but can be implemented by any
15782      * subclass that needs to perform custom logic after a move occurs.
15783      * @param {Number} x The new x position
15784      * @param {Number} y The new y position
15785      */
15786     onPosition : function(x, y){
15787
15788     },
15789
15790     // private
15791     adjustSize : function(w, h){
15792         if(this.autoWidth){
15793             w = 'auto';
15794         }
15795         if(this.autoHeight){
15796             h = 'auto';
15797         }
15798         return {width : w, height: h};
15799     },
15800
15801     // private
15802     adjustPosition : function(x, y){
15803         return {x : x, y: y};
15804     }
15805 });/*
15806  * Original code for Roojs - LGPL
15807  * <script type="text/javascript">
15808  */
15809  
15810 /**
15811  * @class Roo.XComponent
15812  * A delayed Element creator...
15813  * Or a way to group chunks of interface together.
15814  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15815  *  used in conjunction with XComponent.build() it will create an instance of each element,
15816  *  then call addxtype() to build the User interface.
15817  * 
15818  * Mypart.xyx = new Roo.XComponent({
15819
15820     parent : 'Mypart.xyz', // empty == document.element.!!
15821     order : '001',
15822     name : 'xxxx'
15823     region : 'xxxx'
15824     disabled : function() {} 
15825      
15826     tree : function() { // return an tree of xtype declared components
15827         var MODULE = this;
15828         return 
15829         {
15830             xtype : 'NestedLayoutPanel',
15831             // technicall
15832         }
15833      ]
15834  *})
15835  *
15836  *
15837  * It can be used to build a big heiracy, with parent etc.
15838  * or you can just use this to render a single compoent to a dom element
15839  * MYPART.render(Roo.Element | String(id) | dom_element )
15840  *
15841  *
15842  * Usage patterns.
15843  *
15844  * Classic Roo
15845  *
15846  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15847  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15848  *
15849  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15850  *
15851  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15852  * - if mulitple topModules exist, the last one is defined as the top module.
15853  *
15854  * Embeded Roo
15855  * 
15856  * When the top level or multiple modules are to embedded into a existing HTML page,
15857  * the parent element can container '#id' of the element where the module will be drawn.
15858  *
15859  * Bootstrap Roo
15860  *
15861  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15862  * it relies more on a include mechanism, where sub modules are included into an outer page.
15863  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15864  * 
15865  * Bootstrap Roo Included elements
15866  *
15867  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15868  * hence confusing the component builder as it thinks there are multiple top level elements. 
15869  *
15870  * 
15871  * 
15872  * @extends Roo.util.Observable
15873  * @constructor
15874  * @param cfg {Object} configuration of component
15875  * 
15876  */
15877 Roo.XComponent = function(cfg) {
15878     Roo.apply(this, cfg);
15879     this.addEvents({ 
15880         /**
15881              * @event built
15882              * Fires when this the componnt is built
15883              * @param {Roo.XComponent} c the component
15884              */
15885         'built' : true
15886         
15887     });
15888     this.region = this.region || 'center'; // default..
15889     Roo.XComponent.register(this);
15890     this.modules = false;
15891     this.el = false; // where the layout goes..
15892     
15893     
15894 }
15895 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15896     /**
15897      * @property el
15898      * The created element (with Roo.factory())
15899      * @type {Roo.Layout}
15900      */
15901     el  : false,
15902     
15903     /**
15904      * @property el
15905      * for BC  - use el in new code
15906      * @type {Roo.Layout}
15907      */
15908     panel : false,
15909     
15910     /**
15911      * @property layout
15912      * for BC  - use el in new code
15913      * @type {Roo.Layout}
15914      */
15915     layout : false,
15916     
15917      /**
15918      * @cfg {Function|boolean} disabled
15919      * If this module is disabled by some rule, return true from the funtion
15920      */
15921     disabled : false,
15922     
15923     /**
15924      * @cfg {String} parent 
15925      * Name of parent element which it get xtype added to..
15926      */
15927     parent: false,
15928     
15929     /**
15930      * @cfg {String} order
15931      * Used to set the order in which elements are created (usefull for multiple tabs)
15932      */
15933     
15934     order : false,
15935     /**
15936      * @cfg {String} name
15937      * String to display while loading.
15938      */
15939     name : false,
15940     /**
15941      * @cfg {String} region
15942      * Region to render component to (defaults to center)
15943      */
15944     region : 'center',
15945     
15946     /**
15947      * @cfg {Array} items
15948      * A single item array - the first element is the root of the tree..
15949      * It's done this way to stay compatible with the Xtype system...
15950      */
15951     items : false,
15952     
15953     /**
15954      * @property _tree
15955      * The method that retuns the tree of parts that make up this compoennt 
15956      * @type {function}
15957      */
15958     _tree  : false,
15959     
15960      /**
15961      * render
15962      * render element to dom or tree
15963      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15964      */
15965     
15966     render : function(el)
15967     {
15968         
15969         el = el || false;
15970         var hp = this.parent ? 1 : 0;
15971         Roo.debug &&  Roo.log(this);
15972         
15973         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15974             // if parent is a '#.....' string, then let's use that..
15975             var ename = this.parent.substr(1);
15976             this.parent = false;
15977             Roo.debug && Roo.log(ename);
15978             switch (ename) {
15979                 case 'bootstrap-body' :
15980                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15981                         this.parent = { el :  new  Roo.bootstrap.Body() };
15982                         Roo.debug && Roo.log("setting el to doc body");
15983                          
15984                     } else {
15985                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15986                     }
15987                     break;
15988                 case 'bootstrap':
15989                     this.parent = { el : true};
15990                     // fall through
15991                 default:
15992                     el = Roo.get(ename);
15993                     break;
15994             }
15995                 
15996             
15997             if (!el && !this.parent) {
15998                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
15999                 return;
16000             }
16001         }
16002         Roo.debug && Roo.log("EL:");
16003         Roo.debug && Roo.log(el);
16004         Roo.debug && Roo.log("this.parent.el:");
16005         Roo.debug && Roo.log(this.parent.el);
16006         
16007         var tree = this._tree ? this._tree() : this.tree();
16008
16009         // altertive root elements ??? - we need a better way to indicate these.
16010         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16011                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16012         
16013         if (!this.parent && is_alt) {
16014             //el = Roo.get(document.body);
16015             this.parent = { el : true };
16016         }
16017             
16018             
16019         
16020         if (!this.parent) {
16021             
16022             Roo.debug && Roo.log("no parent - creating one");
16023             
16024             el = el ? Roo.get(el) : false;      
16025             
16026             // it's a top level one..
16027             this.parent =  {
16028                 el : new Roo.BorderLayout(el || document.body, {
16029                 
16030                      center: {
16031                          titlebar: false,
16032                          autoScroll:false,
16033                          closeOnTab: true,
16034                          tabPosition: 'top',
16035                           //resizeTabs: true,
16036                          alwaysShowTabs: el && hp? false :  true,
16037                          hideTabs: el || !hp ? true :  false,
16038                          minTabWidth: 140
16039                      }
16040                  })
16041             }
16042         }
16043         
16044         if (!this.parent.el) {
16045                 // probably an old style ctor, which has been disabled.
16046                 return;
16047
16048         }
16049                 // The 'tree' method is  '_tree now' 
16050             
16051         tree.region = tree.region || this.region;
16052         
16053         if (this.parent.el === true) {
16054             // bootstrap... - body..
16055             this.parent.el = Roo.factory(tree);
16056         }
16057         
16058         this.el = this.parent.el.addxtype(tree);
16059         this.fireEvent('built', this);
16060         
16061         this.panel = this.el;
16062         this.layout = this.panel.layout;
16063         this.parentLayout = this.parent.layout  || false;  
16064          
16065     }
16066     
16067 });
16068
16069 Roo.apply(Roo.XComponent, {
16070     /**
16071      * @property  hideProgress
16072      * true to disable the building progress bar.. usefull on single page renders.
16073      * @type Boolean
16074      */
16075     hideProgress : false,
16076     /**
16077      * @property  buildCompleted
16078      * True when the builder has completed building the interface.
16079      * @type Boolean
16080      */
16081     buildCompleted : false,
16082      
16083     /**
16084      * @property  topModule
16085      * the upper most module - uses document.element as it's constructor.
16086      * @type Object
16087      */
16088      
16089     topModule  : false,
16090       
16091     /**
16092      * @property  modules
16093      * array of modules to be created by registration system.
16094      * @type {Array} of Roo.XComponent
16095      */
16096     
16097     modules : [],
16098     /**
16099      * @property  elmodules
16100      * array of modules to be created by which use #ID 
16101      * @type {Array} of Roo.XComponent
16102      */
16103      
16104     elmodules : [],
16105
16106      /**
16107      * @property  build_from_html
16108      * Build elements from html - used by bootstrap HTML stuff 
16109      *    - this is cleared after build is completed
16110      * @type {boolean} true  (default false)
16111      */
16112      
16113     build_from_html : false,
16114
16115     /**
16116      * Register components to be built later.
16117      *
16118      * This solves the following issues
16119      * - Building is not done on page load, but after an authentication process has occured.
16120      * - Interface elements are registered on page load
16121      * - Parent Interface elements may not be loaded before child, so this handles that..
16122      * 
16123      *
16124      * example:
16125      * 
16126      * MyApp.register({
16127           order : '000001',
16128           module : 'Pman.Tab.projectMgr',
16129           region : 'center',
16130           parent : 'Pman.layout',
16131           disabled : false,  // or use a function..
16132         })
16133      
16134      * * @param {Object} details about module
16135      */
16136     register : function(obj) {
16137                 
16138         Roo.XComponent.event.fireEvent('register', obj);
16139         switch(typeof(obj.disabled) ) {
16140                 
16141             case 'undefined':
16142                 break;
16143             
16144             case 'function':
16145                 if ( obj.disabled() ) {
16146                         return;
16147                 }
16148                 break;
16149             
16150             default:
16151                 if (obj.disabled) {
16152                         return;
16153                 }
16154                 break;
16155         }
16156                 
16157         this.modules.push(obj);
16158          
16159     },
16160     /**
16161      * convert a string to an object..
16162      * eg. 'AAA.BBB' -> finds AAA.BBB
16163
16164      */
16165     
16166     toObject : function(str)
16167     {
16168         if (!str || typeof(str) == 'object') {
16169             return str;
16170         }
16171         if (str.substring(0,1) == '#') {
16172             return str;
16173         }
16174
16175         var ar = str.split('.');
16176         var rt, o;
16177         rt = ar.shift();
16178             /** eval:var:o */
16179         try {
16180             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16181         } catch (e) {
16182             throw "Module not found : " + str;
16183         }
16184         
16185         if (o === false) {
16186             throw "Module not found : " + str;
16187         }
16188         Roo.each(ar, function(e) {
16189             if (typeof(o[e]) == 'undefined') {
16190                 throw "Module not found : " + str;
16191             }
16192             o = o[e];
16193         });
16194         
16195         return o;
16196         
16197     },
16198     
16199     
16200     /**
16201      * move modules into their correct place in the tree..
16202      * 
16203      */
16204     preBuild : function ()
16205     {
16206         var _t = this;
16207         Roo.each(this.modules , function (obj)
16208         {
16209             Roo.XComponent.event.fireEvent('beforebuild', obj);
16210             
16211             var opar = obj.parent;
16212             try { 
16213                 obj.parent = this.toObject(opar);
16214             } catch(e) {
16215                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16216                 return;
16217             }
16218             
16219             if (!obj.parent) {
16220                 Roo.debug && Roo.log("GOT top level module");
16221                 Roo.debug && Roo.log(obj);
16222                 obj.modules = new Roo.util.MixedCollection(false, 
16223                     function(o) { return o.order + '' }
16224                 );
16225                 this.topModule = obj;
16226                 return;
16227             }
16228                         // parent is a string (usually a dom element name..)
16229             if (typeof(obj.parent) == 'string') {
16230                 this.elmodules.push(obj);
16231                 return;
16232             }
16233             if (obj.parent.constructor != Roo.XComponent) {
16234                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16235             }
16236             if (!obj.parent.modules) {
16237                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16238                     function(o) { return o.order + '' }
16239                 );
16240             }
16241             if (obj.parent.disabled) {
16242                 obj.disabled = true;
16243             }
16244             obj.parent.modules.add(obj);
16245         }, this);
16246     },
16247     
16248      /**
16249      * make a list of modules to build.
16250      * @return {Array} list of modules. 
16251      */ 
16252     
16253     buildOrder : function()
16254     {
16255         var _this = this;
16256         var cmp = function(a,b) {   
16257             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16258         };
16259         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16260             throw "No top level modules to build";
16261         }
16262         
16263         // make a flat list in order of modules to build.
16264         var mods = this.topModule ? [ this.topModule ] : [];
16265                 
16266         
16267         // elmodules (is a list of DOM based modules )
16268         Roo.each(this.elmodules, function(e) {
16269             mods.push(e);
16270             if (!this.topModule &&
16271                 typeof(e.parent) == 'string' &&
16272                 e.parent.substring(0,1) == '#' &&
16273                 Roo.get(e.parent.substr(1))
16274                ) {
16275                 
16276                 _this.topModule = e;
16277             }
16278             
16279         });
16280
16281         
16282         // add modules to their parents..
16283         var addMod = function(m) {
16284             Roo.debug && Roo.log("build Order: add: " + m.name);
16285                 
16286             mods.push(m);
16287             if (m.modules && !m.disabled) {
16288                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16289                 m.modules.keySort('ASC',  cmp );
16290                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16291     
16292                 m.modules.each(addMod);
16293             } else {
16294                 Roo.debug && Roo.log("build Order: no child modules");
16295             }
16296             // not sure if this is used any more..
16297             if (m.finalize) {
16298                 m.finalize.name = m.name + " (clean up) ";
16299                 mods.push(m.finalize);
16300             }
16301             
16302         }
16303         if (this.topModule && this.topModule.modules) { 
16304             this.topModule.modules.keySort('ASC',  cmp );
16305             this.topModule.modules.each(addMod);
16306         } 
16307         return mods;
16308     },
16309     
16310      /**
16311      * Build the registered modules.
16312      * @param {Object} parent element.
16313      * @param {Function} optional method to call after module has been added.
16314      * 
16315      */ 
16316    
16317     build : function(opts) 
16318     {
16319         
16320         if (typeof(opts) != 'undefined') {
16321             Roo.apply(this,opts);
16322         }
16323         
16324         this.preBuild();
16325         var mods = this.buildOrder();
16326       
16327         //this.allmods = mods;
16328         //Roo.debug && Roo.log(mods);
16329         //return;
16330         if (!mods.length) { // should not happen
16331             throw "NO modules!!!";
16332         }
16333         
16334         
16335         var msg = "Building Interface...";
16336         // flash it up as modal - so we store the mask!?
16337         if (!this.hideProgress && Roo.MessageBox) {
16338             Roo.MessageBox.show({ title: 'loading' });
16339             Roo.MessageBox.show({
16340                title: "Please wait...",
16341                msg: msg,
16342                width:450,
16343                progress:true,
16344                closable:false,
16345                modal: false
16346               
16347             });
16348         }
16349         var total = mods.length;
16350         
16351         var _this = this;
16352         var progressRun = function() {
16353             if (!mods.length) {
16354                 Roo.debug && Roo.log('hide?');
16355                 if (!this.hideProgress && Roo.MessageBox) {
16356                     Roo.MessageBox.hide();
16357                 }
16358                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16359                 
16360                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16361                 
16362                 // THE END...
16363                 return false;   
16364             }
16365             
16366             var m = mods.shift();
16367             
16368             
16369             Roo.debug && Roo.log(m);
16370             // not sure if this is supported any more.. - modules that are are just function
16371             if (typeof(m) == 'function') { 
16372                 m.call(this);
16373                 return progressRun.defer(10, _this);
16374             } 
16375             
16376             
16377             msg = "Building Interface " + (total  - mods.length) + 
16378                     " of " + total + 
16379                     (m.name ? (' - ' + m.name) : '');
16380                         Roo.debug && Roo.log(msg);
16381             if (!this.hideProgress &&  Roo.MessageBox) { 
16382                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16383             }
16384             
16385          
16386             // is the module disabled?
16387             var disabled = (typeof(m.disabled) == 'function') ?
16388                 m.disabled.call(m.module.disabled) : m.disabled;    
16389             
16390             
16391             if (disabled) {
16392                 return progressRun(); // we do not update the display!
16393             }
16394             
16395             // now build 
16396             
16397                         
16398                         
16399             m.render();
16400             // it's 10 on top level, and 1 on others??? why...
16401             return progressRun.defer(10, _this);
16402              
16403         }
16404         progressRun.defer(1, _this);
16405      
16406         
16407         
16408     },
16409         
16410         
16411         /**
16412          * Event Object.
16413          *
16414          *
16415          */
16416         event: false, 
16417     /**
16418          * wrapper for event.on - aliased later..  
16419          * Typically use to register a event handler for register:
16420          *
16421          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16422          *
16423          */
16424     on : false
16425    
16426     
16427     
16428 });
16429
16430 Roo.XComponent.event = new Roo.util.Observable({
16431                 events : { 
16432                         /**
16433                          * @event register
16434                          * Fires when an Component is registered,
16435                          * set the disable property on the Component to stop registration.
16436                          * @param {Roo.XComponent} c the component being registerd.
16437                          * 
16438                          */
16439                         'register' : true,
16440             /**
16441                          * @event beforebuild
16442                          * Fires before each Component is built
16443                          * can be used to apply permissions.
16444                          * @param {Roo.XComponent} c the component being registerd.
16445                          * 
16446                          */
16447                         'beforebuild' : true,
16448                         /**
16449                          * @event buildcomplete
16450                          * Fires on the top level element when all elements have been built
16451                          * @param {Roo.XComponent} the top level component.
16452                          */
16453                         'buildcomplete' : true
16454                         
16455                 }
16456 });
16457
16458 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16459  /*
16460  * Based on:
16461  * Ext JS Library 1.1.1
16462  * Copyright(c) 2006-2007, Ext JS, LLC.
16463  *
16464  * Originally Released Under LGPL - original licence link has changed is not relivant.
16465  *
16466  * Fork - LGPL
16467  * <script type="text/javascript">
16468  */
16469
16470
16471
16472 /*
16473  * These classes are derivatives of the similarly named classes in the YUI Library.
16474  * The original license:
16475  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16476  * Code licensed under the BSD License:
16477  * http://developer.yahoo.net/yui/license.txt
16478  */
16479
16480 (function() {
16481
16482 var Event=Roo.EventManager;
16483 var Dom=Roo.lib.Dom;
16484
16485 /**
16486  * @class Roo.dd.DragDrop
16487  * @extends Roo.util.Observable
16488  * Defines the interface and base operation of items that that can be
16489  * dragged or can be drop targets.  It was designed to be extended, overriding
16490  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16491  * Up to three html elements can be associated with a DragDrop instance:
16492  * <ul>
16493  * <li>linked element: the element that is passed into the constructor.
16494  * This is the element which defines the boundaries for interaction with
16495  * other DragDrop objects.</li>
16496  * <li>handle element(s): The drag operation only occurs if the element that
16497  * was clicked matches a handle element.  By default this is the linked
16498  * element, but there are times that you will want only a portion of the
16499  * linked element to initiate the drag operation, and the setHandleElId()
16500  * method provides a way to define this.</li>
16501  * <li>drag element: this represents the element that would be moved along
16502  * with the cursor during a drag operation.  By default, this is the linked
16503  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16504  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16505  * </li>
16506  * </ul>
16507  * This class should not be instantiated until the onload event to ensure that
16508  * the associated elements are available.
16509  * The following would define a DragDrop obj that would interact with any
16510  * other DragDrop obj in the "group1" group:
16511  * <pre>
16512  *  dd = new Roo.dd.DragDrop("div1", "group1");
16513  * </pre>
16514  * Since none of the event handlers have been implemented, nothing would
16515  * actually happen if you were to run the code above.  Normally you would
16516  * override this class or one of the default implementations, but you can
16517  * also override the methods you want on an instance of the class...
16518  * <pre>
16519  *  dd.onDragDrop = function(e, id) {
16520  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16521  *  }
16522  * </pre>
16523  * @constructor
16524  * @param {String} id of the element that is linked to this instance
16525  * @param {String} sGroup the group of related DragDrop objects
16526  * @param {object} config an object containing configurable attributes
16527  *                Valid properties for DragDrop:
16528  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16529  */
16530 Roo.dd.DragDrop = function(id, sGroup, config) {
16531     if (id) {
16532         this.init(id, sGroup, config);
16533     }
16534     
16535 };
16536
16537 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16538
16539     /**
16540      * The id of the element associated with this object.  This is what we
16541      * refer to as the "linked element" because the size and position of
16542      * this element is used to determine when the drag and drop objects have
16543      * interacted.
16544      * @property id
16545      * @type String
16546      */
16547     id: null,
16548
16549     /**
16550      * Configuration attributes passed into the constructor
16551      * @property config
16552      * @type object
16553      */
16554     config: null,
16555
16556     /**
16557      * The id of the element that will be dragged.  By default this is same
16558      * as the linked element , but could be changed to another element. Ex:
16559      * Roo.dd.DDProxy
16560      * @property dragElId
16561      * @type String
16562      * @private
16563      */
16564     dragElId: null,
16565
16566     /**
16567      * the id of the element that initiates the drag operation.  By default
16568      * this is the linked element, but could be changed to be a child of this
16569      * element.  This lets us do things like only starting the drag when the
16570      * header element within the linked html element is clicked.
16571      * @property handleElId
16572      * @type String
16573      * @private
16574      */
16575     handleElId: null,
16576
16577     /**
16578      * An associative array of HTML tags that will be ignored if clicked.
16579      * @property invalidHandleTypes
16580      * @type {string: string}
16581      */
16582     invalidHandleTypes: null,
16583
16584     /**
16585      * An associative array of ids for elements that will be ignored if clicked
16586      * @property invalidHandleIds
16587      * @type {string: string}
16588      */
16589     invalidHandleIds: null,
16590
16591     /**
16592      * An indexted array of css class names for elements that will be ignored
16593      * if clicked.
16594      * @property invalidHandleClasses
16595      * @type string[]
16596      */
16597     invalidHandleClasses: null,
16598
16599     /**
16600      * The linked element's absolute X position at the time the drag was
16601      * started
16602      * @property startPageX
16603      * @type int
16604      * @private
16605      */
16606     startPageX: 0,
16607
16608     /**
16609      * The linked element's absolute X position at the time the drag was
16610      * started
16611      * @property startPageY
16612      * @type int
16613      * @private
16614      */
16615     startPageY: 0,
16616
16617     /**
16618      * The group defines a logical collection of DragDrop objects that are
16619      * related.  Instances only get events when interacting with other
16620      * DragDrop object in the same group.  This lets us define multiple
16621      * groups using a single DragDrop subclass if we want.
16622      * @property groups
16623      * @type {string: string}
16624      */
16625     groups: null,
16626
16627     /**
16628      * Individual drag/drop instances can be locked.  This will prevent
16629      * onmousedown start drag.
16630      * @property locked
16631      * @type boolean
16632      * @private
16633      */
16634     locked: false,
16635
16636     /**
16637      * Lock this instance
16638      * @method lock
16639      */
16640     lock: function() { this.locked = true; },
16641
16642     /**
16643      * Unlock this instace
16644      * @method unlock
16645      */
16646     unlock: function() { this.locked = false; },
16647
16648     /**
16649      * By default, all insances can be a drop target.  This can be disabled by
16650      * setting isTarget to false.
16651      * @method isTarget
16652      * @type boolean
16653      */
16654     isTarget: true,
16655
16656     /**
16657      * The padding configured for this drag and drop object for calculating
16658      * the drop zone intersection with this object.
16659      * @method padding
16660      * @type int[]
16661      */
16662     padding: null,
16663
16664     /**
16665      * Cached reference to the linked element
16666      * @property _domRef
16667      * @private
16668      */
16669     _domRef: null,
16670
16671     /**
16672      * Internal typeof flag
16673      * @property __ygDragDrop
16674      * @private
16675      */
16676     __ygDragDrop: true,
16677
16678     /**
16679      * Set to true when horizontal contraints are applied
16680      * @property constrainX
16681      * @type boolean
16682      * @private
16683      */
16684     constrainX: false,
16685
16686     /**
16687      * Set to true when vertical contraints are applied
16688      * @property constrainY
16689      * @type boolean
16690      * @private
16691      */
16692     constrainY: false,
16693
16694     /**
16695      * The left constraint
16696      * @property minX
16697      * @type int
16698      * @private
16699      */
16700     minX: 0,
16701
16702     /**
16703      * The right constraint
16704      * @property maxX
16705      * @type int
16706      * @private
16707      */
16708     maxX: 0,
16709
16710     /**
16711      * The up constraint
16712      * @property minY
16713      * @type int
16714      * @type int
16715      * @private
16716      */
16717     minY: 0,
16718
16719     /**
16720      * The down constraint
16721      * @property maxY
16722      * @type int
16723      * @private
16724      */
16725     maxY: 0,
16726
16727     /**
16728      * Maintain offsets when we resetconstraints.  Set to true when you want
16729      * the position of the element relative to its parent to stay the same
16730      * when the page changes
16731      *
16732      * @property maintainOffset
16733      * @type boolean
16734      */
16735     maintainOffset: false,
16736
16737     /**
16738      * Array of pixel locations the element will snap to if we specified a
16739      * horizontal graduation/interval.  This array is generated automatically
16740      * when you define a tick interval.
16741      * @property xTicks
16742      * @type int[]
16743      */
16744     xTicks: null,
16745
16746     /**
16747      * Array of pixel locations the element will snap to if we specified a
16748      * vertical graduation/interval.  This array is generated automatically
16749      * when you define a tick interval.
16750      * @property yTicks
16751      * @type int[]
16752      */
16753     yTicks: null,
16754
16755     /**
16756      * By default the drag and drop instance will only respond to the primary
16757      * button click (left button for a right-handed mouse).  Set to true to
16758      * allow drag and drop to start with any mouse click that is propogated
16759      * by the browser
16760      * @property primaryButtonOnly
16761      * @type boolean
16762      */
16763     primaryButtonOnly: true,
16764
16765     /**
16766      * The availabe property is false until the linked dom element is accessible.
16767      * @property available
16768      * @type boolean
16769      */
16770     available: false,
16771
16772     /**
16773      * By default, drags can only be initiated if the mousedown occurs in the
16774      * region the linked element is.  This is done in part to work around a
16775      * bug in some browsers that mis-report the mousedown if the previous
16776      * mouseup happened outside of the window.  This property is set to true
16777      * if outer handles are defined.
16778      *
16779      * @property hasOuterHandles
16780      * @type boolean
16781      * @default false
16782      */
16783     hasOuterHandles: false,
16784
16785     /**
16786      * Code that executes immediately before the startDrag event
16787      * @method b4StartDrag
16788      * @private
16789      */
16790     b4StartDrag: function(x, y) { },
16791
16792     /**
16793      * Abstract method called after a drag/drop object is clicked
16794      * and the drag or mousedown time thresholds have beeen met.
16795      * @method startDrag
16796      * @param {int} X click location
16797      * @param {int} Y click location
16798      */
16799     startDrag: function(x, y) { /* override this */ },
16800
16801     /**
16802      * Code that executes immediately before the onDrag event
16803      * @method b4Drag
16804      * @private
16805      */
16806     b4Drag: function(e) { },
16807
16808     /**
16809      * Abstract method called during the onMouseMove event while dragging an
16810      * object.
16811      * @method onDrag
16812      * @param {Event} e the mousemove event
16813      */
16814     onDrag: function(e) { /* override this */ },
16815
16816     /**
16817      * Abstract method called when this element fist begins hovering over
16818      * another DragDrop obj
16819      * @method onDragEnter
16820      * @param {Event} e the mousemove event
16821      * @param {String|DragDrop[]} id In POINT mode, the element
16822      * id this is hovering over.  In INTERSECT mode, an array of one or more
16823      * dragdrop items being hovered over.
16824      */
16825     onDragEnter: function(e, id) { /* override this */ },
16826
16827     /**
16828      * Code that executes immediately before the onDragOver event
16829      * @method b4DragOver
16830      * @private
16831      */
16832     b4DragOver: function(e) { },
16833
16834     /**
16835      * Abstract method called when this element is hovering over another
16836      * DragDrop obj
16837      * @method onDragOver
16838      * @param {Event} e the mousemove event
16839      * @param {String|DragDrop[]} id In POINT mode, the element
16840      * id this is hovering over.  In INTERSECT mode, an array of dd items
16841      * being hovered over.
16842      */
16843     onDragOver: function(e, id) { /* override this */ },
16844
16845     /**
16846      * Code that executes immediately before the onDragOut event
16847      * @method b4DragOut
16848      * @private
16849      */
16850     b4DragOut: function(e) { },
16851
16852     /**
16853      * Abstract method called when we are no longer hovering over an element
16854      * @method onDragOut
16855      * @param {Event} e the mousemove event
16856      * @param {String|DragDrop[]} id In POINT mode, the element
16857      * id this was hovering over.  In INTERSECT mode, an array of dd items
16858      * that the mouse is no longer over.
16859      */
16860     onDragOut: function(e, id) { /* override this */ },
16861
16862     /**
16863      * Code that executes immediately before the onDragDrop event
16864      * @method b4DragDrop
16865      * @private
16866      */
16867     b4DragDrop: function(e) { },
16868
16869     /**
16870      * Abstract method called when this item is dropped on another DragDrop
16871      * obj
16872      * @method onDragDrop
16873      * @param {Event} e the mouseup event
16874      * @param {String|DragDrop[]} id In POINT mode, the element
16875      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16876      * was dropped on.
16877      */
16878     onDragDrop: function(e, id) { /* override this */ },
16879
16880     /**
16881      * Abstract method called when this item is dropped on an area with no
16882      * drop target
16883      * @method onInvalidDrop
16884      * @param {Event} e the mouseup event
16885      */
16886     onInvalidDrop: function(e) { /* override this */ },
16887
16888     /**
16889      * Code that executes immediately before the endDrag event
16890      * @method b4EndDrag
16891      * @private
16892      */
16893     b4EndDrag: function(e) { },
16894
16895     /**
16896      * Fired when we are done dragging the object
16897      * @method endDrag
16898      * @param {Event} e the mouseup event
16899      */
16900     endDrag: function(e) { /* override this */ },
16901
16902     /**
16903      * Code executed immediately before the onMouseDown event
16904      * @method b4MouseDown
16905      * @param {Event} e the mousedown event
16906      * @private
16907      */
16908     b4MouseDown: function(e) {  },
16909
16910     /**
16911      * Event handler that fires when a drag/drop obj gets a mousedown
16912      * @method onMouseDown
16913      * @param {Event} e the mousedown event
16914      */
16915     onMouseDown: function(e) { /* override this */ },
16916
16917     /**
16918      * Event handler that fires when a drag/drop obj gets a mouseup
16919      * @method onMouseUp
16920      * @param {Event} e the mouseup event
16921      */
16922     onMouseUp: function(e) { /* override this */ },
16923
16924     /**
16925      * Override the onAvailable method to do what is needed after the initial
16926      * position was determined.
16927      * @method onAvailable
16928      */
16929     onAvailable: function () {
16930     },
16931
16932     /*
16933      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16934      * @type Object
16935      */
16936     defaultPadding : {left:0, right:0, top:0, bottom:0},
16937
16938     /*
16939      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16940  *
16941  * Usage:
16942  <pre><code>
16943  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16944                 { dragElId: "existingProxyDiv" });
16945  dd.startDrag = function(){
16946      this.constrainTo("parent-id");
16947  };
16948  </code></pre>
16949  * Or you can initalize it using the {@link Roo.Element} object:
16950  <pre><code>
16951  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16952      startDrag : function(){
16953          this.constrainTo("parent-id");
16954      }
16955  });
16956  </code></pre>
16957      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16958      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16959      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16960      * an object containing the sides to pad. For example: {right:10, bottom:10}
16961      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16962      */
16963     constrainTo : function(constrainTo, pad, inContent){
16964         if(typeof pad == "number"){
16965             pad = {left: pad, right:pad, top:pad, bottom:pad};
16966         }
16967         pad = pad || this.defaultPadding;
16968         var b = Roo.get(this.getEl()).getBox();
16969         var ce = Roo.get(constrainTo);
16970         var s = ce.getScroll();
16971         var c, cd = ce.dom;
16972         if(cd == document.body){
16973             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16974         }else{
16975             xy = ce.getXY();
16976             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16977         }
16978
16979
16980         var topSpace = b.y - c.y;
16981         var leftSpace = b.x - c.x;
16982
16983         this.resetConstraints();
16984         this.setXConstraint(leftSpace - (pad.left||0), // left
16985                 c.width - leftSpace - b.width - (pad.right||0) //right
16986         );
16987         this.setYConstraint(topSpace - (pad.top||0), //top
16988                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16989         );
16990     },
16991
16992     /**
16993      * Returns a reference to the linked element
16994      * @method getEl
16995      * @return {HTMLElement} the html element
16996      */
16997     getEl: function() {
16998         if (!this._domRef) {
16999             this._domRef = Roo.getDom(this.id);
17000         }
17001
17002         return this._domRef;
17003     },
17004
17005     /**
17006      * Returns a reference to the actual element to drag.  By default this is
17007      * the same as the html element, but it can be assigned to another
17008      * element. An example of this can be found in Roo.dd.DDProxy
17009      * @method getDragEl
17010      * @return {HTMLElement} the html element
17011      */
17012     getDragEl: function() {
17013         return Roo.getDom(this.dragElId);
17014     },
17015
17016     /**
17017      * Sets up the DragDrop object.  Must be called in the constructor of any
17018      * Roo.dd.DragDrop subclass
17019      * @method init
17020      * @param id the id of the linked element
17021      * @param {String} sGroup the group of related items
17022      * @param {object} config configuration attributes
17023      */
17024     init: function(id, sGroup, config) {
17025         this.initTarget(id, sGroup, config);
17026         if (!Roo.isTouch) {
17027             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17028         }
17029         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17030         // Event.on(this.id, "selectstart", Event.preventDefault);
17031     },
17032
17033     /**
17034      * Initializes Targeting functionality only... the object does not
17035      * get a mousedown handler.
17036      * @method initTarget
17037      * @param id the id of the linked element
17038      * @param {String} sGroup the group of related items
17039      * @param {object} config configuration attributes
17040      */
17041     initTarget: function(id, sGroup, config) {
17042
17043         // configuration attributes
17044         this.config = config || {};
17045
17046         // create a local reference to the drag and drop manager
17047         this.DDM = Roo.dd.DDM;
17048         // initialize the groups array
17049         this.groups = {};
17050
17051         // assume that we have an element reference instead of an id if the
17052         // parameter is not a string
17053         if (typeof id !== "string") {
17054             id = Roo.id(id);
17055         }
17056
17057         // set the id
17058         this.id = id;
17059
17060         // add to an interaction group
17061         this.addToGroup((sGroup) ? sGroup : "default");
17062
17063         // We don't want to register this as the handle with the manager
17064         // so we just set the id rather than calling the setter.
17065         this.handleElId = id;
17066
17067         // the linked element is the element that gets dragged by default
17068         this.setDragElId(id);
17069
17070         // by default, clicked anchors will not start drag operations.
17071         this.invalidHandleTypes = { A: "A" };
17072         this.invalidHandleIds = {};
17073         this.invalidHandleClasses = [];
17074
17075         this.applyConfig();
17076
17077         this.handleOnAvailable();
17078     },
17079
17080     /**
17081      * Applies the configuration parameters that were passed into the constructor.
17082      * This is supposed to happen at each level through the inheritance chain.  So
17083      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17084      * DragDrop in order to get all of the parameters that are available in
17085      * each object.
17086      * @method applyConfig
17087      */
17088     applyConfig: function() {
17089
17090         // configurable properties:
17091         //    padding, isTarget, maintainOffset, primaryButtonOnly
17092         this.padding           = this.config.padding || [0, 0, 0, 0];
17093         this.isTarget          = (this.config.isTarget !== false);
17094         this.maintainOffset    = (this.config.maintainOffset);
17095         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17096
17097     },
17098
17099     /**
17100      * Executed when the linked element is available
17101      * @method handleOnAvailable
17102      * @private
17103      */
17104     handleOnAvailable: function() {
17105         this.available = true;
17106         this.resetConstraints();
17107         this.onAvailable();
17108     },
17109
17110      /**
17111      * Configures the padding for the target zone in px.  Effectively expands
17112      * (or reduces) the virtual object size for targeting calculations.
17113      * Supports css-style shorthand; if only one parameter is passed, all sides
17114      * will have that padding, and if only two are passed, the top and bottom
17115      * will have the first param, the left and right the second.
17116      * @method setPadding
17117      * @param {int} iTop    Top pad
17118      * @param {int} iRight  Right pad
17119      * @param {int} iBot    Bot pad
17120      * @param {int} iLeft   Left pad
17121      */
17122     setPadding: function(iTop, iRight, iBot, iLeft) {
17123         // this.padding = [iLeft, iRight, iTop, iBot];
17124         if (!iRight && 0 !== iRight) {
17125             this.padding = [iTop, iTop, iTop, iTop];
17126         } else if (!iBot && 0 !== iBot) {
17127             this.padding = [iTop, iRight, iTop, iRight];
17128         } else {
17129             this.padding = [iTop, iRight, iBot, iLeft];
17130         }
17131     },
17132
17133     /**
17134      * Stores the initial placement of the linked element.
17135      * @method setInitialPosition
17136      * @param {int} diffX   the X offset, default 0
17137      * @param {int} diffY   the Y offset, default 0
17138      */
17139     setInitPosition: function(diffX, diffY) {
17140         var el = this.getEl();
17141
17142         if (!this.DDM.verifyEl(el)) {
17143             return;
17144         }
17145
17146         var dx = diffX || 0;
17147         var dy = diffY || 0;
17148
17149         var p = Dom.getXY( el );
17150
17151         this.initPageX = p[0] - dx;
17152         this.initPageY = p[1] - dy;
17153
17154         this.lastPageX = p[0];
17155         this.lastPageY = p[1];
17156
17157
17158         this.setStartPosition(p);
17159     },
17160
17161     /**
17162      * Sets the start position of the element.  This is set when the obj
17163      * is initialized, the reset when a drag is started.
17164      * @method setStartPosition
17165      * @param pos current position (from previous lookup)
17166      * @private
17167      */
17168     setStartPosition: function(pos) {
17169         var p = pos || Dom.getXY( this.getEl() );
17170         this.deltaSetXY = null;
17171
17172         this.startPageX = p[0];
17173         this.startPageY = p[1];
17174     },
17175
17176     /**
17177      * Add this instance to a group of related drag/drop objects.  All
17178      * instances belong to at least one group, and can belong to as many
17179      * groups as needed.
17180      * @method addToGroup
17181      * @param sGroup {string} the name of the group
17182      */
17183     addToGroup: function(sGroup) {
17184         this.groups[sGroup] = true;
17185         this.DDM.regDragDrop(this, sGroup);
17186     },
17187
17188     /**
17189      * Remove's this instance from the supplied interaction group
17190      * @method removeFromGroup
17191      * @param {string}  sGroup  The group to drop
17192      */
17193     removeFromGroup: function(sGroup) {
17194         if (this.groups[sGroup]) {
17195             delete this.groups[sGroup];
17196         }
17197
17198         this.DDM.removeDDFromGroup(this, sGroup);
17199     },
17200
17201     /**
17202      * Allows you to specify that an element other than the linked element
17203      * will be moved with the cursor during a drag
17204      * @method setDragElId
17205      * @param id {string} the id of the element that will be used to initiate the drag
17206      */
17207     setDragElId: function(id) {
17208         this.dragElId = id;
17209     },
17210
17211     /**
17212      * Allows you to specify a child of the linked element that should be
17213      * used to initiate the drag operation.  An example of this would be if
17214      * you have a content div with text and links.  Clicking anywhere in the
17215      * content area would normally start the drag operation.  Use this method
17216      * to specify that an element inside of the content div is the element
17217      * that starts the drag operation.
17218      * @method setHandleElId
17219      * @param id {string} the id of the element that will be used to
17220      * initiate the drag.
17221      */
17222     setHandleElId: function(id) {
17223         if (typeof id !== "string") {
17224             id = Roo.id(id);
17225         }
17226         this.handleElId = id;
17227         this.DDM.regHandle(this.id, id);
17228     },
17229
17230     /**
17231      * Allows you to set an element outside of the linked element as a drag
17232      * handle
17233      * @method setOuterHandleElId
17234      * @param id the id of the element that will be used to initiate the drag
17235      */
17236     setOuterHandleElId: function(id) {
17237         if (typeof id !== "string") {
17238             id = Roo.id(id);
17239         }
17240         Event.on(id, "mousedown",
17241                 this.handleMouseDown, this);
17242         this.setHandleElId(id);
17243
17244         this.hasOuterHandles = true;
17245     },
17246
17247     /**
17248      * Remove all drag and drop hooks for this element
17249      * @method unreg
17250      */
17251     unreg: function() {
17252         Event.un(this.id, "mousedown",
17253                 this.handleMouseDown);
17254         Event.un(this.id, "touchstart",
17255                 this.handleMouseDown);
17256         this._domRef = null;
17257         this.DDM._remove(this);
17258     },
17259
17260     destroy : function(){
17261         this.unreg();
17262     },
17263
17264     /**
17265      * Returns true if this instance is locked, or the drag drop mgr is locked
17266      * (meaning that all drag/drop is disabled on the page.)
17267      * @method isLocked
17268      * @return {boolean} true if this obj or all drag/drop is locked, else
17269      * false
17270      */
17271     isLocked: function() {
17272         return (this.DDM.isLocked() || this.locked);
17273     },
17274
17275     /**
17276      * Fired when this object is clicked
17277      * @method handleMouseDown
17278      * @param {Event} e
17279      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17280      * @private
17281      */
17282     handleMouseDown: function(e, oDD){
17283      
17284         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17285             //Roo.log('not touch/ button !=0');
17286             return;
17287         }
17288         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17289             return; // double touch..
17290         }
17291         
17292
17293         if (this.isLocked()) {
17294             //Roo.log('locked');
17295             return;
17296         }
17297
17298         this.DDM.refreshCache(this.groups);
17299 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17300         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17301         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17302             //Roo.log('no outer handes or not over target');
17303                 // do nothing.
17304         } else {
17305 //            Roo.log('check validator');
17306             if (this.clickValidator(e)) {
17307 //                Roo.log('validate success');
17308                 // set the initial element position
17309                 this.setStartPosition();
17310
17311
17312                 this.b4MouseDown(e);
17313                 this.onMouseDown(e);
17314
17315                 this.DDM.handleMouseDown(e, this);
17316
17317                 this.DDM.stopEvent(e);
17318             } else {
17319
17320
17321             }
17322         }
17323     },
17324
17325     clickValidator: function(e) {
17326         var target = e.getTarget();
17327         return ( this.isValidHandleChild(target) &&
17328                     (this.id == this.handleElId ||
17329                         this.DDM.handleWasClicked(target, this.id)) );
17330     },
17331
17332     /**
17333      * Allows you to specify a tag name that should not start a drag operation
17334      * when clicked.  This is designed to facilitate embedding links within a
17335      * drag handle that do something other than start the drag.
17336      * @method addInvalidHandleType
17337      * @param {string} tagName the type of element to exclude
17338      */
17339     addInvalidHandleType: function(tagName) {
17340         var type = tagName.toUpperCase();
17341         this.invalidHandleTypes[type] = type;
17342     },
17343
17344     /**
17345      * Lets you to specify an element id for a child of a drag handle
17346      * that should not initiate a drag
17347      * @method addInvalidHandleId
17348      * @param {string} id the element id of the element you wish to ignore
17349      */
17350     addInvalidHandleId: function(id) {
17351         if (typeof id !== "string") {
17352             id = Roo.id(id);
17353         }
17354         this.invalidHandleIds[id] = id;
17355     },
17356
17357     /**
17358      * Lets you specify a css class of elements that will not initiate a drag
17359      * @method addInvalidHandleClass
17360      * @param {string} cssClass the class of the elements you wish to ignore
17361      */
17362     addInvalidHandleClass: function(cssClass) {
17363         this.invalidHandleClasses.push(cssClass);
17364     },
17365
17366     /**
17367      * Unsets an excluded tag name set by addInvalidHandleType
17368      * @method removeInvalidHandleType
17369      * @param {string} tagName the type of element to unexclude
17370      */
17371     removeInvalidHandleType: function(tagName) {
17372         var type = tagName.toUpperCase();
17373         // this.invalidHandleTypes[type] = null;
17374         delete this.invalidHandleTypes[type];
17375     },
17376
17377     /**
17378      * Unsets an invalid handle id
17379      * @method removeInvalidHandleId
17380      * @param {string} id the id of the element to re-enable
17381      */
17382     removeInvalidHandleId: function(id) {
17383         if (typeof id !== "string") {
17384             id = Roo.id(id);
17385         }
17386         delete this.invalidHandleIds[id];
17387     },
17388
17389     /**
17390      * Unsets an invalid css class
17391      * @method removeInvalidHandleClass
17392      * @param {string} cssClass the class of the element(s) you wish to
17393      * re-enable
17394      */
17395     removeInvalidHandleClass: function(cssClass) {
17396         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17397             if (this.invalidHandleClasses[i] == cssClass) {
17398                 delete this.invalidHandleClasses[i];
17399             }
17400         }
17401     },
17402
17403     /**
17404      * Checks the tag exclusion list to see if this click should be ignored
17405      * @method isValidHandleChild
17406      * @param {HTMLElement} node the HTMLElement to evaluate
17407      * @return {boolean} true if this is a valid tag type, false if not
17408      */
17409     isValidHandleChild: function(node) {
17410
17411         var valid = true;
17412         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17413         var nodeName;
17414         try {
17415             nodeName = node.nodeName.toUpperCase();
17416         } catch(e) {
17417             nodeName = node.nodeName;
17418         }
17419         valid = valid && !this.invalidHandleTypes[nodeName];
17420         valid = valid && !this.invalidHandleIds[node.id];
17421
17422         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17423             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17424         }
17425
17426
17427         return valid;
17428
17429     },
17430
17431     /**
17432      * Create the array of horizontal tick marks if an interval was specified
17433      * in setXConstraint().
17434      * @method setXTicks
17435      * @private
17436      */
17437     setXTicks: function(iStartX, iTickSize) {
17438         this.xTicks = [];
17439         this.xTickSize = iTickSize;
17440
17441         var tickMap = {};
17442
17443         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17444             if (!tickMap[i]) {
17445                 this.xTicks[this.xTicks.length] = i;
17446                 tickMap[i] = true;
17447             }
17448         }
17449
17450         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17451             if (!tickMap[i]) {
17452                 this.xTicks[this.xTicks.length] = i;
17453                 tickMap[i] = true;
17454             }
17455         }
17456
17457         this.xTicks.sort(this.DDM.numericSort) ;
17458     },
17459
17460     /**
17461      * Create the array of vertical tick marks if an interval was specified in
17462      * setYConstraint().
17463      * @method setYTicks
17464      * @private
17465      */
17466     setYTicks: function(iStartY, iTickSize) {
17467         this.yTicks = [];
17468         this.yTickSize = iTickSize;
17469
17470         var tickMap = {};
17471
17472         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17473             if (!tickMap[i]) {
17474                 this.yTicks[this.yTicks.length] = i;
17475                 tickMap[i] = true;
17476             }
17477         }
17478
17479         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17480             if (!tickMap[i]) {
17481                 this.yTicks[this.yTicks.length] = i;
17482                 tickMap[i] = true;
17483             }
17484         }
17485
17486         this.yTicks.sort(this.DDM.numericSort) ;
17487     },
17488
17489     /**
17490      * By default, the element can be dragged any place on the screen.  Use
17491      * this method to limit the horizontal travel of the element.  Pass in
17492      * 0,0 for the parameters if you want to lock the drag to the y axis.
17493      * @method setXConstraint
17494      * @param {int} iLeft the number of pixels the element can move to the left
17495      * @param {int} iRight the number of pixels the element can move to the
17496      * right
17497      * @param {int} iTickSize optional parameter for specifying that the
17498      * element
17499      * should move iTickSize pixels at a time.
17500      */
17501     setXConstraint: function(iLeft, iRight, iTickSize) {
17502         this.leftConstraint = iLeft;
17503         this.rightConstraint = iRight;
17504
17505         this.minX = this.initPageX - iLeft;
17506         this.maxX = this.initPageX + iRight;
17507         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17508
17509         this.constrainX = true;
17510     },
17511
17512     /**
17513      * Clears any constraints applied to this instance.  Also clears ticks
17514      * since they can't exist independent of a constraint at this time.
17515      * @method clearConstraints
17516      */
17517     clearConstraints: function() {
17518         this.constrainX = false;
17519         this.constrainY = false;
17520         this.clearTicks();
17521     },
17522
17523     /**
17524      * Clears any tick interval defined for this instance
17525      * @method clearTicks
17526      */
17527     clearTicks: function() {
17528         this.xTicks = null;
17529         this.yTicks = null;
17530         this.xTickSize = 0;
17531         this.yTickSize = 0;
17532     },
17533
17534     /**
17535      * By default, the element can be dragged any place on the screen.  Set
17536      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17537      * parameters if you want to lock the drag to the x axis.
17538      * @method setYConstraint
17539      * @param {int} iUp the number of pixels the element can move up
17540      * @param {int} iDown the number of pixels the element can move down
17541      * @param {int} iTickSize optional parameter for specifying that the
17542      * element should move iTickSize pixels at a time.
17543      */
17544     setYConstraint: function(iUp, iDown, iTickSize) {
17545         this.topConstraint = iUp;
17546         this.bottomConstraint = iDown;
17547
17548         this.minY = this.initPageY - iUp;
17549         this.maxY = this.initPageY + iDown;
17550         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17551
17552         this.constrainY = true;
17553
17554     },
17555
17556     /**
17557      * resetConstraints must be called if you manually reposition a dd element.
17558      * @method resetConstraints
17559      * @param {boolean} maintainOffset
17560      */
17561     resetConstraints: function() {
17562
17563
17564         // Maintain offsets if necessary
17565         if (this.initPageX || this.initPageX === 0) {
17566             // figure out how much this thing has moved
17567             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17568             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17569
17570             this.setInitPosition(dx, dy);
17571
17572         // This is the first time we have detected the element's position
17573         } else {
17574             this.setInitPosition();
17575         }
17576
17577         if (this.constrainX) {
17578             this.setXConstraint( this.leftConstraint,
17579                                  this.rightConstraint,
17580                                  this.xTickSize        );
17581         }
17582
17583         if (this.constrainY) {
17584             this.setYConstraint( this.topConstraint,
17585                                  this.bottomConstraint,
17586                                  this.yTickSize         );
17587         }
17588     },
17589
17590     /**
17591      * Normally the drag element is moved pixel by pixel, but we can specify
17592      * that it move a number of pixels at a time.  This method resolves the
17593      * location when we have it set up like this.
17594      * @method getTick
17595      * @param {int} val where we want to place the object
17596      * @param {int[]} tickArray sorted array of valid points
17597      * @return {int} the closest tick
17598      * @private
17599      */
17600     getTick: function(val, tickArray) {
17601
17602         if (!tickArray) {
17603             // If tick interval is not defined, it is effectively 1 pixel,
17604             // so we return the value passed to us.
17605             return val;
17606         } else if (tickArray[0] >= val) {
17607             // The value is lower than the first tick, so we return the first
17608             // tick.
17609             return tickArray[0];
17610         } else {
17611             for (var i=0, len=tickArray.length; i<len; ++i) {
17612                 var next = i + 1;
17613                 if (tickArray[next] && tickArray[next] >= val) {
17614                     var diff1 = val - tickArray[i];
17615                     var diff2 = tickArray[next] - val;
17616                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17617                 }
17618             }
17619
17620             // The value is larger than the last tick, so we return the last
17621             // tick.
17622             return tickArray[tickArray.length - 1];
17623         }
17624     },
17625
17626     /**
17627      * toString method
17628      * @method toString
17629      * @return {string} string representation of the dd obj
17630      */
17631     toString: function() {
17632         return ("DragDrop " + this.id);
17633     }
17634
17635 });
17636
17637 })();
17638 /*
17639  * Based on:
17640  * Ext JS Library 1.1.1
17641  * Copyright(c) 2006-2007, Ext JS, LLC.
17642  *
17643  * Originally Released Under LGPL - original licence link has changed is not relivant.
17644  *
17645  * Fork - LGPL
17646  * <script type="text/javascript">
17647  */
17648
17649
17650 /**
17651  * The drag and drop utility provides a framework for building drag and drop
17652  * applications.  In addition to enabling drag and drop for specific elements,
17653  * the drag and drop elements are tracked by the manager class, and the
17654  * interactions between the various elements are tracked during the drag and
17655  * the implementing code is notified about these important moments.
17656  */
17657
17658 // Only load the library once.  Rewriting the manager class would orphan
17659 // existing drag and drop instances.
17660 if (!Roo.dd.DragDropMgr) {
17661
17662 /**
17663  * @class Roo.dd.DragDropMgr
17664  * DragDropMgr is a singleton that tracks the element interaction for
17665  * all DragDrop items in the window.  Generally, you will not call
17666  * this class directly, but it does have helper methods that could
17667  * be useful in your DragDrop implementations.
17668  * @singleton
17669  */
17670 Roo.dd.DragDropMgr = function() {
17671
17672     var Event = Roo.EventManager;
17673
17674     return {
17675
17676         /**
17677          * Two dimensional Array of registered DragDrop objects.  The first
17678          * dimension is the DragDrop item group, the second the DragDrop
17679          * object.
17680          * @property ids
17681          * @type {string: string}
17682          * @private
17683          * @static
17684          */
17685         ids: {},
17686
17687         /**
17688          * Array of element ids defined as drag handles.  Used to determine
17689          * if the element that generated the mousedown event is actually the
17690          * handle and not the html element itself.
17691          * @property handleIds
17692          * @type {string: string}
17693          * @private
17694          * @static
17695          */
17696         handleIds: {},
17697
17698         /**
17699          * the DragDrop object that is currently being dragged
17700          * @property dragCurrent
17701          * @type DragDrop
17702          * @private
17703          * @static
17704          **/
17705         dragCurrent: null,
17706
17707         /**
17708          * the DragDrop object(s) that are being hovered over
17709          * @property dragOvers
17710          * @type Array
17711          * @private
17712          * @static
17713          */
17714         dragOvers: {},
17715
17716         /**
17717          * the X distance between the cursor and the object being dragged
17718          * @property deltaX
17719          * @type int
17720          * @private
17721          * @static
17722          */
17723         deltaX: 0,
17724
17725         /**
17726          * the Y distance between the cursor and the object being dragged
17727          * @property deltaY
17728          * @type int
17729          * @private
17730          * @static
17731          */
17732         deltaY: 0,
17733
17734         /**
17735          * Flag to determine if we should prevent the default behavior of the
17736          * events we define. By default this is true, but this can be set to
17737          * false if you need the default behavior (not recommended)
17738          * @property preventDefault
17739          * @type boolean
17740          * @static
17741          */
17742         preventDefault: true,
17743
17744         /**
17745          * Flag to determine if we should stop the propagation of the events
17746          * we generate. This is true by default but you may want to set it to
17747          * false if the html element contains other features that require the
17748          * mouse click.
17749          * @property stopPropagation
17750          * @type boolean
17751          * @static
17752          */
17753         stopPropagation: true,
17754
17755         /**
17756          * Internal flag that is set to true when drag and drop has been
17757          * intialized
17758          * @property initialized
17759          * @private
17760          * @static
17761          */
17762         initalized: false,
17763
17764         /**
17765          * All drag and drop can be disabled.
17766          * @property locked
17767          * @private
17768          * @static
17769          */
17770         locked: false,
17771
17772         /**
17773          * Called the first time an element is registered.
17774          * @method init
17775          * @private
17776          * @static
17777          */
17778         init: function() {
17779             this.initialized = true;
17780         },
17781
17782         /**
17783          * In point mode, drag and drop interaction is defined by the
17784          * location of the cursor during the drag/drop
17785          * @property POINT
17786          * @type int
17787          * @static
17788          */
17789         POINT: 0,
17790
17791         /**
17792          * In intersect mode, drag and drop interactio nis defined by the
17793          * overlap of two or more drag and drop objects.
17794          * @property INTERSECT
17795          * @type int
17796          * @static
17797          */
17798         INTERSECT: 1,
17799
17800         /**
17801          * The current drag and drop mode.  Default: POINT
17802          * @property mode
17803          * @type int
17804          * @static
17805          */
17806         mode: 0,
17807
17808         /**
17809          * Runs method on all drag and drop objects
17810          * @method _execOnAll
17811          * @private
17812          * @static
17813          */
17814         _execOnAll: function(sMethod, args) {
17815             for (var i in this.ids) {
17816                 for (var j in this.ids[i]) {
17817                     var oDD = this.ids[i][j];
17818                     if (! this.isTypeOfDD(oDD)) {
17819                         continue;
17820                     }
17821                     oDD[sMethod].apply(oDD, args);
17822                 }
17823             }
17824         },
17825
17826         /**
17827          * Drag and drop initialization.  Sets up the global event handlers
17828          * @method _onLoad
17829          * @private
17830          * @static
17831          */
17832         _onLoad: function() {
17833
17834             this.init();
17835
17836             if (!Roo.isTouch) {
17837                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17838                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17839             }
17840             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17841             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17842             
17843             Event.on(window,   "unload",    this._onUnload, this, true);
17844             Event.on(window,   "resize",    this._onResize, this, true);
17845             // Event.on(window,   "mouseout",    this._test);
17846
17847         },
17848
17849         /**
17850          * Reset constraints on all drag and drop objs
17851          * @method _onResize
17852          * @private
17853          * @static
17854          */
17855         _onResize: function(e) {
17856             this._execOnAll("resetConstraints", []);
17857         },
17858
17859         /**
17860          * Lock all drag and drop functionality
17861          * @method lock
17862          * @static
17863          */
17864         lock: function() { this.locked = true; },
17865
17866         /**
17867          * Unlock all drag and drop functionality
17868          * @method unlock
17869          * @static
17870          */
17871         unlock: function() { this.locked = false; },
17872
17873         /**
17874          * Is drag and drop locked?
17875          * @method isLocked
17876          * @return {boolean} True if drag and drop is locked, false otherwise.
17877          * @static
17878          */
17879         isLocked: function() { return this.locked; },
17880
17881         /**
17882          * Location cache that is set for all drag drop objects when a drag is
17883          * initiated, cleared when the drag is finished.
17884          * @property locationCache
17885          * @private
17886          * @static
17887          */
17888         locationCache: {},
17889
17890         /**
17891          * Set useCache to false if you want to force object the lookup of each
17892          * drag and drop linked element constantly during a drag.
17893          * @property useCache
17894          * @type boolean
17895          * @static
17896          */
17897         useCache: true,
17898
17899         /**
17900          * The number of pixels that the mouse needs to move after the
17901          * mousedown before the drag is initiated.  Default=3;
17902          * @property clickPixelThresh
17903          * @type int
17904          * @static
17905          */
17906         clickPixelThresh: 3,
17907
17908         /**
17909          * The number of milliseconds after the mousedown event to initiate the
17910          * drag if we don't get a mouseup event. Default=1000
17911          * @property clickTimeThresh
17912          * @type int
17913          * @static
17914          */
17915         clickTimeThresh: 350,
17916
17917         /**
17918          * Flag that indicates that either the drag pixel threshold or the
17919          * mousdown time threshold has been met
17920          * @property dragThreshMet
17921          * @type boolean
17922          * @private
17923          * @static
17924          */
17925         dragThreshMet: false,
17926
17927         /**
17928          * Timeout used for the click time threshold
17929          * @property clickTimeout
17930          * @type Object
17931          * @private
17932          * @static
17933          */
17934         clickTimeout: null,
17935
17936         /**
17937          * The X position of the mousedown event stored for later use when a
17938          * drag threshold is met.
17939          * @property startX
17940          * @type int
17941          * @private
17942          * @static
17943          */
17944         startX: 0,
17945
17946         /**
17947          * The Y position of the mousedown event stored for later use when a
17948          * drag threshold is met.
17949          * @property startY
17950          * @type int
17951          * @private
17952          * @static
17953          */
17954         startY: 0,
17955
17956         /**
17957          * Each DragDrop instance must be registered with the DragDropMgr.
17958          * This is executed in DragDrop.init()
17959          * @method regDragDrop
17960          * @param {DragDrop} oDD the DragDrop object to register
17961          * @param {String} sGroup the name of the group this element belongs to
17962          * @static
17963          */
17964         regDragDrop: function(oDD, sGroup) {
17965             if (!this.initialized) { this.init(); }
17966
17967             if (!this.ids[sGroup]) {
17968                 this.ids[sGroup] = {};
17969             }
17970             this.ids[sGroup][oDD.id] = oDD;
17971         },
17972
17973         /**
17974          * Removes the supplied dd instance from the supplied group. Executed
17975          * by DragDrop.removeFromGroup, so don't call this function directly.
17976          * @method removeDDFromGroup
17977          * @private
17978          * @static
17979          */
17980         removeDDFromGroup: function(oDD, sGroup) {
17981             if (!this.ids[sGroup]) {
17982                 this.ids[sGroup] = {};
17983             }
17984
17985             var obj = this.ids[sGroup];
17986             if (obj && obj[oDD.id]) {
17987                 delete obj[oDD.id];
17988             }
17989         },
17990
17991         /**
17992          * Unregisters a drag and drop item.  This is executed in
17993          * DragDrop.unreg, use that method instead of calling this directly.
17994          * @method _remove
17995          * @private
17996          * @static
17997          */
17998         _remove: function(oDD) {
17999             for (var g in oDD.groups) {
18000                 if (g && this.ids[g][oDD.id]) {
18001                     delete this.ids[g][oDD.id];
18002                 }
18003             }
18004             delete this.handleIds[oDD.id];
18005         },
18006
18007         /**
18008          * Each DragDrop handle element must be registered.  This is done
18009          * automatically when executing DragDrop.setHandleElId()
18010          * @method regHandle
18011          * @param {String} sDDId the DragDrop id this element is a handle for
18012          * @param {String} sHandleId the id of the element that is the drag
18013          * handle
18014          * @static
18015          */
18016         regHandle: function(sDDId, sHandleId) {
18017             if (!this.handleIds[sDDId]) {
18018                 this.handleIds[sDDId] = {};
18019             }
18020             this.handleIds[sDDId][sHandleId] = sHandleId;
18021         },
18022
18023         /**
18024          * Utility function to determine if a given element has been
18025          * registered as a drag drop item.
18026          * @method isDragDrop
18027          * @param {String} id the element id to check
18028          * @return {boolean} true if this element is a DragDrop item,
18029          * false otherwise
18030          * @static
18031          */
18032         isDragDrop: function(id) {
18033             return ( this.getDDById(id) ) ? true : false;
18034         },
18035
18036         /**
18037          * Returns the drag and drop instances that are in all groups the
18038          * passed in instance belongs to.
18039          * @method getRelated
18040          * @param {DragDrop} p_oDD the obj to get related data for
18041          * @param {boolean} bTargetsOnly if true, only return targetable objs
18042          * @return {DragDrop[]} the related instances
18043          * @static
18044          */
18045         getRelated: function(p_oDD, bTargetsOnly) {
18046             var oDDs = [];
18047             for (var i in p_oDD.groups) {
18048                 for (j in this.ids[i]) {
18049                     var dd = this.ids[i][j];
18050                     if (! this.isTypeOfDD(dd)) {
18051                         continue;
18052                     }
18053                     if (!bTargetsOnly || dd.isTarget) {
18054                         oDDs[oDDs.length] = dd;
18055                     }
18056                 }
18057             }
18058
18059             return oDDs;
18060         },
18061
18062         /**
18063          * Returns true if the specified dd target is a legal target for
18064          * the specifice drag obj
18065          * @method isLegalTarget
18066          * @param {DragDrop} the drag obj
18067          * @param {DragDrop} the target
18068          * @return {boolean} true if the target is a legal target for the
18069          * dd obj
18070          * @static
18071          */
18072         isLegalTarget: function (oDD, oTargetDD) {
18073             var targets = this.getRelated(oDD, true);
18074             for (var i=0, len=targets.length;i<len;++i) {
18075                 if (targets[i].id == oTargetDD.id) {
18076                     return true;
18077                 }
18078             }
18079
18080             return false;
18081         },
18082
18083         /**
18084          * My goal is to be able to transparently determine if an object is
18085          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18086          * returns "object", oDD.constructor.toString() always returns
18087          * "DragDrop" and not the name of the subclass.  So for now it just
18088          * evaluates a well-known variable in DragDrop.
18089          * @method isTypeOfDD
18090          * @param {Object} the object to evaluate
18091          * @return {boolean} true if typeof oDD = DragDrop
18092          * @static
18093          */
18094         isTypeOfDD: function (oDD) {
18095             return (oDD && oDD.__ygDragDrop);
18096         },
18097
18098         /**
18099          * Utility function to determine if a given element has been
18100          * registered as a drag drop handle for the given Drag Drop object.
18101          * @method isHandle
18102          * @param {String} id the element id to check
18103          * @return {boolean} true if this element is a DragDrop handle, false
18104          * otherwise
18105          * @static
18106          */
18107         isHandle: function(sDDId, sHandleId) {
18108             return ( this.handleIds[sDDId] &&
18109                             this.handleIds[sDDId][sHandleId] );
18110         },
18111
18112         /**
18113          * Returns the DragDrop instance for a given id
18114          * @method getDDById
18115          * @param {String} id the id of the DragDrop object
18116          * @return {DragDrop} the drag drop object, null if it is not found
18117          * @static
18118          */
18119         getDDById: function(id) {
18120             for (var i in this.ids) {
18121                 if (this.ids[i][id]) {
18122                     return this.ids[i][id];
18123                 }
18124             }
18125             return null;
18126         },
18127
18128         /**
18129          * Fired after a registered DragDrop object gets the mousedown event.
18130          * Sets up the events required to track the object being dragged
18131          * @method handleMouseDown
18132          * @param {Event} e the event
18133          * @param oDD the DragDrop object being dragged
18134          * @private
18135          * @static
18136          */
18137         handleMouseDown: function(e, oDD) {
18138             if(Roo.QuickTips){
18139                 Roo.QuickTips.disable();
18140             }
18141             this.currentTarget = e.getTarget();
18142
18143             this.dragCurrent = oDD;
18144
18145             var el = oDD.getEl();
18146
18147             // track start position
18148             this.startX = e.getPageX();
18149             this.startY = e.getPageY();
18150
18151             this.deltaX = this.startX - el.offsetLeft;
18152             this.deltaY = this.startY - el.offsetTop;
18153
18154             this.dragThreshMet = false;
18155
18156             this.clickTimeout = setTimeout(
18157                     function() {
18158                         var DDM = Roo.dd.DDM;
18159                         DDM.startDrag(DDM.startX, DDM.startY);
18160                     },
18161                     this.clickTimeThresh );
18162         },
18163
18164         /**
18165          * Fired when either the drag pixel threshol or the mousedown hold
18166          * time threshold has been met.
18167          * @method startDrag
18168          * @param x {int} the X position of the original mousedown
18169          * @param y {int} the Y position of the original mousedown
18170          * @static
18171          */
18172         startDrag: function(x, y) {
18173             clearTimeout(this.clickTimeout);
18174             if (this.dragCurrent) {
18175                 this.dragCurrent.b4StartDrag(x, y);
18176                 this.dragCurrent.startDrag(x, y);
18177             }
18178             this.dragThreshMet = true;
18179         },
18180
18181         /**
18182          * Internal function to handle the mouseup event.  Will be invoked
18183          * from the context of the document.
18184          * @method handleMouseUp
18185          * @param {Event} e the event
18186          * @private
18187          * @static
18188          */
18189         handleMouseUp: function(e) {
18190
18191             if(Roo.QuickTips){
18192                 Roo.QuickTips.enable();
18193             }
18194             if (! this.dragCurrent) {
18195                 return;
18196             }
18197
18198             clearTimeout(this.clickTimeout);
18199
18200             if (this.dragThreshMet) {
18201                 this.fireEvents(e, true);
18202             } else {
18203             }
18204
18205             this.stopDrag(e);
18206
18207             this.stopEvent(e);
18208         },
18209
18210         /**
18211          * Utility to stop event propagation and event default, if these
18212          * features are turned on.
18213          * @method stopEvent
18214          * @param {Event} e the event as returned by this.getEvent()
18215          * @static
18216          */
18217         stopEvent: function(e){
18218             if(this.stopPropagation) {
18219                 e.stopPropagation();
18220             }
18221
18222             if (this.preventDefault) {
18223                 e.preventDefault();
18224             }
18225         },
18226
18227         /**
18228          * Internal function to clean up event handlers after the drag
18229          * operation is complete
18230          * @method stopDrag
18231          * @param {Event} e the event
18232          * @private
18233          * @static
18234          */
18235         stopDrag: function(e) {
18236             // Fire the drag end event for the item that was dragged
18237             if (this.dragCurrent) {
18238                 if (this.dragThreshMet) {
18239                     this.dragCurrent.b4EndDrag(e);
18240                     this.dragCurrent.endDrag(e);
18241                 }
18242
18243                 this.dragCurrent.onMouseUp(e);
18244             }
18245
18246             this.dragCurrent = null;
18247             this.dragOvers = {};
18248         },
18249
18250         /**
18251          * Internal function to handle the mousemove event.  Will be invoked
18252          * from the context of the html element.
18253          *
18254          * @TODO figure out what we can do about mouse events lost when the
18255          * user drags objects beyond the window boundary.  Currently we can
18256          * detect this in internet explorer by verifying that the mouse is
18257          * down during the mousemove event.  Firefox doesn't give us the
18258          * button state on the mousemove event.
18259          * @method handleMouseMove
18260          * @param {Event} e the event
18261          * @private
18262          * @static
18263          */
18264         handleMouseMove: function(e) {
18265             if (! this.dragCurrent) {
18266                 return true;
18267             }
18268
18269             // var button = e.which || e.button;
18270
18271             // check for IE mouseup outside of page boundary
18272             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18273                 this.stopEvent(e);
18274                 return this.handleMouseUp(e);
18275             }
18276
18277             if (!this.dragThreshMet) {
18278                 var diffX = Math.abs(this.startX - e.getPageX());
18279                 var diffY = Math.abs(this.startY - e.getPageY());
18280                 if (diffX > this.clickPixelThresh ||
18281                             diffY > this.clickPixelThresh) {
18282                     this.startDrag(this.startX, this.startY);
18283                 }
18284             }
18285
18286             if (this.dragThreshMet) {
18287                 this.dragCurrent.b4Drag(e);
18288                 this.dragCurrent.onDrag(e);
18289                 if(!this.dragCurrent.moveOnly){
18290                     this.fireEvents(e, false);
18291                 }
18292             }
18293
18294             this.stopEvent(e);
18295
18296             return true;
18297         },
18298
18299         /**
18300          * Iterates over all of the DragDrop elements to find ones we are
18301          * hovering over or dropping on
18302          * @method fireEvents
18303          * @param {Event} e the event
18304          * @param {boolean} isDrop is this a drop op or a mouseover op?
18305          * @private
18306          * @static
18307          */
18308         fireEvents: function(e, isDrop) {
18309             var dc = this.dragCurrent;
18310
18311             // If the user did the mouse up outside of the window, we could
18312             // get here even though we have ended the drag.
18313             if (!dc || dc.isLocked()) {
18314                 return;
18315             }
18316
18317             var pt = e.getPoint();
18318
18319             // cache the previous dragOver array
18320             var oldOvers = [];
18321
18322             var outEvts   = [];
18323             var overEvts  = [];
18324             var dropEvts  = [];
18325             var enterEvts = [];
18326
18327             // Check to see if the object(s) we were hovering over is no longer
18328             // being hovered over so we can fire the onDragOut event
18329             for (var i in this.dragOvers) {
18330
18331                 var ddo = this.dragOvers[i];
18332
18333                 if (! this.isTypeOfDD(ddo)) {
18334                     continue;
18335                 }
18336
18337                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18338                     outEvts.push( ddo );
18339                 }
18340
18341                 oldOvers[i] = true;
18342                 delete this.dragOvers[i];
18343             }
18344
18345             for (var sGroup in dc.groups) {
18346
18347                 if ("string" != typeof sGroup) {
18348                     continue;
18349                 }
18350
18351                 for (i in this.ids[sGroup]) {
18352                     var oDD = this.ids[sGroup][i];
18353                     if (! this.isTypeOfDD(oDD)) {
18354                         continue;
18355                     }
18356
18357                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18358                         if (this.isOverTarget(pt, oDD, this.mode)) {
18359                             // look for drop interactions
18360                             if (isDrop) {
18361                                 dropEvts.push( oDD );
18362                             // look for drag enter and drag over interactions
18363                             } else {
18364
18365                                 // initial drag over: dragEnter fires
18366                                 if (!oldOvers[oDD.id]) {
18367                                     enterEvts.push( oDD );
18368                                 // subsequent drag overs: dragOver fires
18369                                 } else {
18370                                     overEvts.push( oDD );
18371                                 }
18372
18373                                 this.dragOvers[oDD.id] = oDD;
18374                             }
18375                         }
18376                     }
18377                 }
18378             }
18379
18380             if (this.mode) {
18381                 if (outEvts.length) {
18382                     dc.b4DragOut(e, outEvts);
18383                     dc.onDragOut(e, outEvts);
18384                 }
18385
18386                 if (enterEvts.length) {
18387                     dc.onDragEnter(e, enterEvts);
18388                 }
18389
18390                 if (overEvts.length) {
18391                     dc.b4DragOver(e, overEvts);
18392                     dc.onDragOver(e, overEvts);
18393                 }
18394
18395                 if (dropEvts.length) {
18396                     dc.b4DragDrop(e, dropEvts);
18397                     dc.onDragDrop(e, dropEvts);
18398                 }
18399
18400             } else {
18401                 // fire dragout events
18402                 var len = 0;
18403                 for (i=0, len=outEvts.length; i<len; ++i) {
18404                     dc.b4DragOut(e, outEvts[i].id);
18405                     dc.onDragOut(e, outEvts[i].id);
18406                 }
18407
18408                 // fire enter events
18409                 for (i=0,len=enterEvts.length; i<len; ++i) {
18410                     // dc.b4DragEnter(e, oDD.id);
18411                     dc.onDragEnter(e, enterEvts[i].id);
18412                 }
18413
18414                 // fire over events
18415                 for (i=0,len=overEvts.length; i<len; ++i) {
18416                     dc.b4DragOver(e, overEvts[i].id);
18417                     dc.onDragOver(e, overEvts[i].id);
18418                 }
18419
18420                 // fire drop events
18421                 for (i=0, len=dropEvts.length; i<len; ++i) {
18422                     dc.b4DragDrop(e, dropEvts[i].id);
18423                     dc.onDragDrop(e, dropEvts[i].id);
18424                 }
18425
18426             }
18427
18428             // notify about a drop that did not find a target
18429             if (isDrop && !dropEvts.length) {
18430                 dc.onInvalidDrop(e);
18431             }
18432
18433         },
18434
18435         /**
18436          * Helper function for getting the best match from the list of drag
18437          * and drop objects returned by the drag and drop events when we are
18438          * in INTERSECT mode.  It returns either the first object that the
18439          * cursor is over, or the object that has the greatest overlap with
18440          * the dragged element.
18441          * @method getBestMatch
18442          * @param  {DragDrop[]} dds The array of drag and drop objects
18443          * targeted
18444          * @return {DragDrop}       The best single match
18445          * @static
18446          */
18447         getBestMatch: function(dds) {
18448             var winner = null;
18449             // Return null if the input is not what we expect
18450             //if (!dds || !dds.length || dds.length == 0) {
18451                // winner = null;
18452             // If there is only one item, it wins
18453             //} else if (dds.length == 1) {
18454
18455             var len = dds.length;
18456
18457             if (len == 1) {
18458                 winner = dds[0];
18459             } else {
18460                 // Loop through the targeted items
18461                 for (var i=0; i<len; ++i) {
18462                     var dd = dds[i];
18463                     // If the cursor is over the object, it wins.  If the
18464                     // cursor is over multiple matches, the first one we come
18465                     // to wins.
18466                     if (dd.cursorIsOver) {
18467                         winner = dd;
18468                         break;
18469                     // Otherwise the object with the most overlap wins
18470                     } else {
18471                         if (!winner ||
18472                             winner.overlap.getArea() < dd.overlap.getArea()) {
18473                             winner = dd;
18474                         }
18475                     }
18476                 }
18477             }
18478
18479             return winner;
18480         },
18481
18482         /**
18483          * Refreshes the cache of the top-left and bottom-right points of the
18484          * drag and drop objects in the specified group(s).  This is in the
18485          * format that is stored in the drag and drop instance, so typical
18486          * usage is:
18487          * <code>
18488          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18489          * </code>
18490          * Alternatively:
18491          * <code>
18492          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18493          * </code>
18494          * @TODO this really should be an indexed array.  Alternatively this
18495          * method could accept both.
18496          * @method refreshCache
18497          * @param {Object} groups an associative array of groups to refresh
18498          * @static
18499          */
18500         refreshCache: function(groups) {
18501             for (var sGroup in groups) {
18502                 if ("string" != typeof sGroup) {
18503                     continue;
18504                 }
18505                 for (var i in this.ids[sGroup]) {
18506                     var oDD = this.ids[sGroup][i];
18507
18508                     if (this.isTypeOfDD(oDD)) {
18509                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18510                         var loc = this.getLocation(oDD);
18511                         if (loc) {
18512                             this.locationCache[oDD.id] = loc;
18513                         } else {
18514                             delete this.locationCache[oDD.id];
18515                             // this will unregister the drag and drop object if
18516                             // the element is not in a usable state
18517                             // oDD.unreg();
18518                         }
18519                     }
18520                 }
18521             }
18522         },
18523
18524         /**
18525          * This checks to make sure an element exists and is in the DOM.  The
18526          * main purpose is to handle cases where innerHTML is used to remove
18527          * drag and drop objects from the DOM.  IE provides an 'unspecified
18528          * error' when trying to access the offsetParent of such an element
18529          * @method verifyEl
18530          * @param {HTMLElement} el the element to check
18531          * @return {boolean} true if the element looks usable
18532          * @static
18533          */
18534         verifyEl: function(el) {
18535             if (el) {
18536                 var parent;
18537                 if(Roo.isIE){
18538                     try{
18539                         parent = el.offsetParent;
18540                     }catch(e){}
18541                 }else{
18542                     parent = el.offsetParent;
18543                 }
18544                 if (parent) {
18545                     return true;
18546                 }
18547             }
18548
18549             return false;
18550         },
18551
18552         /**
18553          * Returns a Region object containing the drag and drop element's position
18554          * and size, including the padding configured for it
18555          * @method getLocation
18556          * @param {DragDrop} oDD the drag and drop object to get the
18557          *                       location for
18558          * @return {Roo.lib.Region} a Region object representing the total area
18559          *                             the element occupies, including any padding
18560          *                             the instance is configured for.
18561          * @static
18562          */
18563         getLocation: function(oDD) {
18564             if (! this.isTypeOfDD(oDD)) {
18565                 return null;
18566             }
18567
18568             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18569
18570             try {
18571                 pos= Roo.lib.Dom.getXY(el);
18572             } catch (e) { }
18573
18574             if (!pos) {
18575                 return null;
18576             }
18577
18578             x1 = pos[0];
18579             x2 = x1 + el.offsetWidth;
18580             y1 = pos[1];
18581             y2 = y1 + el.offsetHeight;
18582
18583             t = y1 - oDD.padding[0];
18584             r = x2 + oDD.padding[1];
18585             b = y2 + oDD.padding[2];
18586             l = x1 - oDD.padding[3];
18587
18588             return new Roo.lib.Region( t, r, b, l );
18589         },
18590
18591         /**
18592          * Checks the cursor location to see if it over the target
18593          * @method isOverTarget
18594          * @param {Roo.lib.Point} pt The point to evaluate
18595          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18596          * @return {boolean} true if the mouse is over the target
18597          * @private
18598          * @static
18599          */
18600         isOverTarget: function(pt, oTarget, intersect) {
18601             // use cache if available
18602             var loc = this.locationCache[oTarget.id];
18603             if (!loc || !this.useCache) {
18604                 loc = this.getLocation(oTarget);
18605                 this.locationCache[oTarget.id] = loc;
18606
18607             }
18608
18609             if (!loc) {
18610                 return false;
18611             }
18612
18613             oTarget.cursorIsOver = loc.contains( pt );
18614
18615             // DragDrop is using this as a sanity check for the initial mousedown
18616             // in this case we are done.  In POINT mode, if the drag obj has no
18617             // contraints, we are also done. Otherwise we need to evaluate the
18618             // location of the target as related to the actual location of the
18619             // dragged element.
18620             var dc = this.dragCurrent;
18621             if (!dc || !dc.getTargetCoord ||
18622                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18623                 return oTarget.cursorIsOver;
18624             }
18625
18626             oTarget.overlap = null;
18627
18628             // Get the current location of the drag element, this is the
18629             // location of the mouse event less the delta that represents
18630             // where the original mousedown happened on the element.  We
18631             // need to consider constraints and ticks as well.
18632             var pos = dc.getTargetCoord(pt.x, pt.y);
18633
18634             var el = dc.getDragEl();
18635             var curRegion = new Roo.lib.Region( pos.y,
18636                                                    pos.x + el.offsetWidth,
18637                                                    pos.y + el.offsetHeight,
18638                                                    pos.x );
18639
18640             var overlap = curRegion.intersect(loc);
18641
18642             if (overlap) {
18643                 oTarget.overlap = overlap;
18644                 return (intersect) ? true : oTarget.cursorIsOver;
18645             } else {
18646                 return false;
18647             }
18648         },
18649
18650         /**
18651          * unload event handler
18652          * @method _onUnload
18653          * @private
18654          * @static
18655          */
18656         _onUnload: function(e, me) {
18657             Roo.dd.DragDropMgr.unregAll();
18658         },
18659
18660         /**
18661          * Cleans up the drag and drop events and objects.
18662          * @method unregAll
18663          * @private
18664          * @static
18665          */
18666         unregAll: function() {
18667
18668             if (this.dragCurrent) {
18669                 this.stopDrag();
18670                 this.dragCurrent = null;
18671             }
18672
18673             this._execOnAll("unreg", []);
18674
18675             for (i in this.elementCache) {
18676                 delete this.elementCache[i];
18677             }
18678
18679             this.elementCache = {};
18680             this.ids = {};
18681         },
18682
18683         /**
18684          * A cache of DOM elements
18685          * @property elementCache
18686          * @private
18687          * @static
18688          */
18689         elementCache: {},
18690
18691         /**
18692          * Get the wrapper for the DOM element specified
18693          * @method getElWrapper
18694          * @param {String} id the id of the element to get
18695          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18696          * @private
18697          * @deprecated This wrapper isn't that useful
18698          * @static
18699          */
18700         getElWrapper: function(id) {
18701             var oWrapper = this.elementCache[id];
18702             if (!oWrapper || !oWrapper.el) {
18703                 oWrapper = this.elementCache[id] =
18704                     new this.ElementWrapper(Roo.getDom(id));
18705             }
18706             return oWrapper;
18707         },
18708
18709         /**
18710          * Returns the actual DOM element
18711          * @method getElement
18712          * @param {String} id the id of the elment to get
18713          * @return {Object} The element
18714          * @deprecated use Roo.getDom instead
18715          * @static
18716          */
18717         getElement: function(id) {
18718             return Roo.getDom(id);
18719         },
18720
18721         /**
18722          * Returns the style property for the DOM element (i.e.,
18723          * document.getElById(id).style)
18724          * @method getCss
18725          * @param {String} id the id of the elment to get
18726          * @return {Object} The style property of the element
18727          * @deprecated use Roo.getDom instead
18728          * @static
18729          */
18730         getCss: function(id) {
18731             var el = Roo.getDom(id);
18732             return (el) ? el.style : null;
18733         },
18734
18735         /**
18736          * Inner class for cached elements
18737          * @class DragDropMgr.ElementWrapper
18738          * @for DragDropMgr
18739          * @private
18740          * @deprecated
18741          */
18742         ElementWrapper: function(el) {
18743                 /**
18744                  * The element
18745                  * @property el
18746                  */
18747                 this.el = el || null;
18748                 /**
18749                  * The element id
18750                  * @property id
18751                  */
18752                 this.id = this.el && el.id;
18753                 /**
18754                  * A reference to the style property
18755                  * @property css
18756                  */
18757                 this.css = this.el && el.style;
18758             },
18759
18760         /**
18761          * Returns the X position of an html element
18762          * @method getPosX
18763          * @param el the element for which to get the position
18764          * @return {int} the X coordinate
18765          * @for DragDropMgr
18766          * @deprecated use Roo.lib.Dom.getX instead
18767          * @static
18768          */
18769         getPosX: function(el) {
18770             return Roo.lib.Dom.getX(el);
18771         },
18772
18773         /**
18774          * Returns the Y position of an html element
18775          * @method getPosY
18776          * @param el the element for which to get the position
18777          * @return {int} the Y coordinate
18778          * @deprecated use Roo.lib.Dom.getY instead
18779          * @static
18780          */
18781         getPosY: function(el) {
18782             return Roo.lib.Dom.getY(el);
18783         },
18784
18785         /**
18786          * Swap two nodes.  In IE, we use the native method, for others we
18787          * emulate the IE behavior
18788          * @method swapNode
18789          * @param n1 the first node to swap
18790          * @param n2 the other node to swap
18791          * @static
18792          */
18793         swapNode: function(n1, n2) {
18794             if (n1.swapNode) {
18795                 n1.swapNode(n2);
18796             } else {
18797                 var p = n2.parentNode;
18798                 var s = n2.nextSibling;
18799
18800                 if (s == n1) {
18801                     p.insertBefore(n1, n2);
18802                 } else if (n2 == n1.nextSibling) {
18803                     p.insertBefore(n2, n1);
18804                 } else {
18805                     n1.parentNode.replaceChild(n2, n1);
18806                     p.insertBefore(n1, s);
18807                 }
18808             }
18809         },
18810
18811         /**
18812          * Returns the current scroll position
18813          * @method getScroll
18814          * @private
18815          * @static
18816          */
18817         getScroll: function () {
18818             var t, l, dde=document.documentElement, db=document.body;
18819             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18820                 t = dde.scrollTop;
18821                 l = dde.scrollLeft;
18822             } else if (db) {
18823                 t = db.scrollTop;
18824                 l = db.scrollLeft;
18825             } else {
18826
18827             }
18828             return { top: t, left: l };
18829         },
18830
18831         /**
18832          * Returns the specified element style property
18833          * @method getStyle
18834          * @param {HTMLElement} el          the element
18835          * @param {string}      styleProp   the style property
18836          * @return {string} The value of the style property
18837          * @deprecated use Roo.lib.Dom.getStyle
18838          * @static
18839          */
18840         getStyle: function(el, styleProp) {
18841             return Roo.fly(el).getStyle(styleProp);
18842         },
18843
18844         /**
18845          * Gets the scrollTop
18846          * @method getScrollTop
18847          * @return {int} the document's scrollTop
18848          * @static
18849          */
18850         getScrollTop: function () { return this.getScroll().top; },
18851
18852         /**
18853          * Gets the scrollLeft
18854          * @method getScrollLeft
18855          * @return {int} the document's scrollTop
18856          * @static
18857          */
18858         getScrollLeft: function () { return this.getScroll().left; },
18859
18860         /**
18861          * Sets the x/y position of an element to the location of the
18862          * target element.
18863          * @method moveToEl
18864          * @param {HTMLElement} moveEl      The element to move
18865          * @param {HTMLElement} targetEl    The position reference element
18866          * @static
18867          */
18868         moveToEl: function (moveEl, targetEl) {
18869             var aCoord = Roo.lib.Dom.getXY(targetEl);
18870             Roo.lib.Dom.setXY(moveEl, aCoord);
18871         },
18872
18873         /**
18874          * Numeric array sort function
18875          * @method numericSort
18876          * @static
18877          */
18878         numericSort: function(a, b) { return (a - b); },
18879
18880         /**
18881          * Internal counter
18882          * @property _timeoutCount
18883          * @private
18884          * @static
18885          */
18886         _timeoutCount: 0,
18887
18888         /**
18889          * Trying to make the load order less important.  Without this we get
18890          * an error if this file is loaded before the Event Utility.
18891          * @method _addListeners
18892          * @private
18893          * @static
18894          */
18895         _addListeners: function() {
18896             var DDM = Roo.dd.DDM;
18897             if ( Roo.lib.Event && document ) {
18898                 DDM._onLoad();
18899             } else {
18900                 if (DDM._timeoutCount > 2000) {
18901                 } else {
18902                     setTimeout(DDM._addListeners, 10);
18903                     if (document && document.body) {
18904                         DDM._timeoutCount += 1;
18905                     }
18906                 }
18907             }
18908         },
18909
18910         /**
18911          * Recursively searches the immediate parent and all child nodes for
18912          * the handle element in order to determine wheter or not it was
18913          * clicked.
18914          * @method handleWasClicked
18915          * @param node the html element to inspect
18916          * @static
18917          */
18918         handleWasClicked: function(node, id) {
18919             if (this.isHandle(id, node.id)) {
18920                 return true;
18921             } else {
18922                 // check to see if this is a text node child of the one we want
18923                 var p = node.parentNode;
18924
18925                 while (p) {
18926                     if (this.isHandle(id, p.id)) {
18927                         return true;
18928                     } else {
18929                         p = p.parentNode;
18930                     }
18931                 }
18932             }
18933
18934             return false;
18935         }
18936
18937     };
18938
18939 }();
18940
18941 // shorter alias, save a few bytes
18942 Roo.dd.DDM = Roo.dd.DragDropMgr;
18943 Roo.dd.DDM._addListeners();
18944
18945 }/*
18946  * Based on:
18947  * Ext JS Library 1.1.1
18948  * Copyright(c) 2006-2007, Ext JS, LLC.
18949  *
18950  * Originally Released Under LGPL - original licence link has changed is not relivant.
18951  *
18952  * Fork - LGPL
18953  * <script type="text/javascript">
18954  */
18955
18956 /**
18957  * @class Roo.dd.DD
18958  * A DragDrop implementation where the linked element follows the
18959  * mouse cursor during a drag.
18960  * @extends Roo.dd.DragDrop
18961  * @constructor
18962  * @param {String} id the id of the linked element
18963  * @param {String} sGroup the group of related DragDrop items
18964  * @param {object} config an object containing configurable attributes
18965  *                Valid properties for DD:
18966  *                    scroll
18967  */
18968 Roo.dd.DD = function(id, sGroup, config) {
18969     if (id) {
18970         this.init(id, sGroup, config);
18971     }
18972 };
18973
18974 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18975
18976     /**
18977      * When set to true, the utility automatically tries to scroll the browser
18978      * window wehn a drag and drop element is dragged near the viewport boundary.
18979      * Defaults to true.
18980      * @property scroll
18981      * @type boolean
18982      */
18983     scroll: true,
18984
18985     /**
18986      * Sets the pointer offset to the distance between the linked element's top
18987      * left corner and the location the element was clicked
18988      * @method autoOffset
18989      * @param {int} iPageX the X coordinate of the click
18990      * @param {int} iPageY the Y coordinate of the click
18991      */
18992     autoOffset: function(iPageX, iPageY) {
18993         var x = iPageX - this.startPageX;
18994         var y = iPageY - this.startPageY;
18995         this.setDelta(x, y);
18996     },
18997
18998     /**
18999      * Sets the pointer offset.  You can call this directly to force the
19000      * offset to be in a particular location (e.g., pass in 0,0 to set it
19001      * to the center of the object)
19002      * @method setDelta
19003      * @param {int} iDeltaX the distance from the left
19004      * @param {int} iDeltaY the distance from the top
19005      */
19006     setDelta: function(iDeltaX, iDeltaY) {
19007         this.deltaX = iDeltaX;
19008         this.deltaY = iDeltaY;
19009     },
19010
19011     /**
19012      * Sets the drag element to the location of the mousedown or click event,
19013      * maintaining the cursor location relative to the location on the element
19014      * that was clicked.  Override this if you want to place the element in a
19015      * location other than where the cursor is.
19016      * @method setDragElPos
19017      * @param {int} iPageX the X coordinate of the mousedown or drag event
19018      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19019      */
19020     setDragElPos: function(iPageX, iPageY) {
19021         // the first time we do this, we are going to check to make sure
19022         // the element has css positioning
19023
19024         var el = this.getDragEl();
19025         this.alignElWithMouse(el, iPageX, iPageY);
19026     },
19027
19028     /**
19029      * Sets the element to the location of the mousedown or click event,
19030      * maintaining the cursor location relative to the location on the element
19031      * that was clicked.  Override this if you want to place the element in a
19032      * location other than where the cursor is.
19033      * @method alignElWithMouse
19034      * @param {HTMLElement} el the element to move
19035      * @param {int} iPageX the X coordinate of the mousedown or drag event
19036      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19037      */
19038     alignElWithMouse: function(el, iPageX, iPageY) {
19039         var oCoord = this.getTargetCoord(iPageX, iPageY);
19040         var fly = el.dom ? el : Roo.fly(el);
19041         if (!this.deltaSetXY) {
19042             var aCoord = [oCoord.x, oCoord.y];
19043             fly.setXY(aCoord);
19044             var newLeft = fly.getLeft(true);
19045             var newTop  = fly.getTop(true);
19046             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19047         } else {
19048             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19049         }
19050
19051         this.cachePosition(oCoord.x, oCoord.y);
19052         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19053         return oCoord;
19054     },
19055
19056     /**
19057      * Saves the most recent position so that we can reset the constraints and
19058      * tick marks on-demand.  We need to know this so that we can calculate the
19059      * number of pixels the element is offset from its original position.
19060      * @method cachePosition
19061      * @param iPageX the current x position (optional, this just makes it so we
19062      * don't have to look it up again)
19063      * @param iPageY the current y position (optional, this just makes it so we
19064      * don't have to look it up again)
19065      */
19066     cachePosition: function(iPageX, iPageY) {
19067         if (iPageX) {
19068             this.lastPageX = iPageX;
19069             this.lastPageY = iPageY;
19070         } else {
19071             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19072             this.lastPageX = aCoord[0];
19073             this.lastPageY = aCoord[1];
19074         }
19075     },
19076
19077     /**
19078      * Auto-scroll the window if the dragged object has been moved beyond the
19079      * visible window boundary.
19080      * @method autoScroll
19081      * @param {int} x the drag element's x position
19082      * @param {int} y the drag element's y position
19083      * @param {int} h the height of the drag element
19084      * @param {int} w the width of the drag element
19085      * @private
19086      */
19087     autoScroll: function(x, y, h, w) {
19088
19089         if (this.scroll) {
19090             // The client height
19091             var clientH = Roo.lib.Dom.getViewWidth();
19092
19093             // The client width
19094             var clientW = Roo.lib.Dom.getViewHeight();
19095
19096             // The amt scrolled down
19097             var st = this.DDM.getScrollTop();
19098
19099             // The amt scrolled right
19100             var sl = this.DDM.getScrollLeft();
19101
19102             // Location of the bottom of the element
19103             var bot = h + y;
19104
19105             // Location of the right of the element
19106             var right = w + x;
19107
19108             // The distance from the cursor to the bottom of the visible area,
19109             // adjusted so that we don't scroll if the cursor is beyond the
19110             // element drag constraints
19111             var toBot = (clientH + st - y - this.deltaY);
19112
19113             // The distance from the cursor to the right of the visible area
19114             var toRight = (clientW + sl - x - this.deltaX);
19115
19116
19117             // How close to the edge the cursor must be before we scroll
19118             // var thresh = (document.all) ? 100 : 40;
19119             var thresh = 40;
19120
19121             // How many pixels to scroll per autoscroll op.  This helps to reduce
19122             // clunky scrolling. IE is more sensitive about this ... it needs this
19123             // value to be higher.
19124             var scrAmt = (document.all) ? 80 : 30;
19125
19126             // Scroll down if we are near the bottom of the visible page and the
19127             // obj extends below the crease
19128             if ( bot > clientH && toBot < thresh ) {
19129                 window.scrollTo(sl, st + scrAmt);
19130             }
19131
19132             // Scroll up if the window is scrolled down and the top of the object
19133             // goes above the top border
19134             if ( y < st && st > 0 && y - st < thresh ) {
19135                 window.scrollTo(sl, st - scrAmt);
19136             }
19137
19138             // Scroll right if the obj is beyond the right border and the cursor is
19139             // near the border.
19140             if ( right > clientW && toRight < thresh ) {
19141                 window.scrollTo(sl + scrAmt, st);
19142             }
19143
19144             // Scroll left if the window has been scrolled to the right and the obj
19145             // extends past the left border
19146             if ( x < sl && sl > 0 && x - sl < thresh ) {
19147                 window.scrollTo(sl - scrAmt, st);
19148             }
19149         }
19150     },
19151
19152     /**
19153      * Finds the location the element should be placed if we want to move
19154      * it to where the mouse location less the click offset would place us.
19155      * @method getTargetCoord
19156      * @param {int} iPageX the X coordinate of the click
19157      * @param {int} iPageY the Y coordinate of the click
19158      * @return an object that contains the coordinates (Object.x and Object.y)
19159      * @private
19160      */
19161     getTargetCoord: function(iPageX, iPageY) {
19162
19163
19164         var x = iPageX - this.deltaX;
19165         var y = iPageY - this.deltaY;
19166
19167         if (this.constrainX) {
19168             if (x < this.minX) { x = this.minX; }
19169             if (x > this.maxX) { x = this.maxX; }
19170         }
19171
19172         if (this.constrainY) {
19173             if (y < this.minY) { y = this.minY; }
19174             if (y > this.maxY) { y = this.maxY; }
19175         }
19176
19177         x = this.getTick(x, this.xTicks);
19178         y = this.getTick(y, this.yTicks);
19179
19180
19181         return {x:x, y:y};
19182     },
19183
19184     /*
19185      * Sets up config options specific to this class. Overrides
19186      * Roo.dd.DragDrop, but all versions of this method through the
19187      * inheritance chain are called
19188      */
19189     applyConfig: function() {
19190         Roo.dd.DD.superclass.applyConfig.call(this);
19191         this.scroll = (this.config.scroll !== false);
19192     },
19193
19194     /*
19195      * Event that fires prior to the onMouseDown event.  Overrides
19196      * Roo.dd.DragDrop.
19197      */
19198     b4MouseDown: function(e) {
19199         // this.resetConstraints();
19200         this.autoOffset(e.getPageX(),
19201                             e.getPageY());
19202     },
19203
19204     /*
19205      * Event that fires prior to the onDrag event.  Overrides
19206      * Roo.dd.DragDrop.
19207      */
19208     b4Drag: function(e) {
19209         this.setDragElPos(e.getPageX(),
19210                             e.getPageY());
19211     },
19212
19213     toString: function() {
19214         return ("DD " + this.id);
19215     }
19216
19217     //////////////////////////////////////////////////////////////////////////
19218     // Debugging ygDragDrop events that can be overridden
19219     //////////////////////////////////////////////////////////////////////////
19220     /*
19221     startDrag: function(x, y) {
19222     },
19223
19224     onDrag: function(e) {
19225     },
19226
19227     onDragEnter: function(e, id) {
19228     },
19229
19230     onDragOver: function(e, id) {
19231     },
19232
19233     onDragOut: function(e, id) {
19234     },
19235
19236     onDragDrop: function(e, id) {
19237     },
19238
19239     endDrag: function(e) {
19240     }
19241
19242     */
19243
19244 });/*
19245  * Based on:
19246  * Ext JS Library 1.1.1
19247  * Copyright(c) 2006-2007, Ext JS, LLC.
19248  *
19249  * Originally Released Under LGPL - original licence link has changed is not relivant.
19250  *
19251  * Fork - LGPL
19252  * <script type="text/javascript">
19253  */
19254
19255 /**
19256  * @class Roo.dd.DDProxy
19257  * A DragDrop implementation that inserts an empty, bordered div into
19258  * the document that follows the cursor during drag operations.  At the time of
19259  * the click, the frame div is resized to the dimensions of the linked html
19260  * element, and moved to the exact location of the linked element.
19261  *
19262  * References to the "frame" element refer to the single proxy element that
19263  * was created to be dragged in place of all DDProxy elements on the
19264  * page.
19265  *
19266  * @extends Roo.dd.DD
19267  * @constructor
19268  * @param {String} id the id of the linked html element
19269  * @param {String} sGroup the group of related DragDrop objects
19270  * @param {object} config an object containing configurable attributes
19271  *                Valid properties for DDProxy in addition to those in DragDrop:
19272  *                   resizeFrame, centerFrame, dragElId
19273  */
19274 Roo.dd.DDProxy = function(id, sGroup, config) {
19275     if (id) {
19276         this.init(id, sGroup, config);
19277         this.initFrame();
19278     }
19279 };
19280
19281 /**
19282  * The default drag frame div id
19283  * @property Roo.dd.DDProxy.dragElId
19284  * @type String
19285  * @static
19286  */
19287 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19288
19289 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19290
19291     /**
19292      * By default we resize the drag frame to be the same size as the element
19293      * we want to drag (this is to get the frame effect).  We can turn it off
19294      * if we want a different behavior.
19295      * @property resizeFrame
19296      * @type boolean
19297      */
19298     resizeFrame: true,
19299
19300     /**
19301      * By default the frame is positioned exactly where the drag element is, so
19302      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19303      * you do not have constraints on the obj is to have the drag frame centered
19304      * around the cursor.  Set centerFrame to true for this effect.
19305      * @property centerFrame
19306      * @type boolean
19307      */
19308     centerFrame: false,
19309
19310     /**
19311      * Creates the proxy element if it does not yet exist
19312      * @method createFrame
19313      */
19314     createFrame: function() {
19315         var self = this;
19316         var body = document.body;
19317
19318         if (!body || !body.firstChild) {
19319             setTimeout( function() { self.createFrame(); }, 50 );
19320             return;
19321         }
19322
19323         var div = this.getDragEl();
19324
19325         if (!div) {
19326             div    = document.createElement("div");
19327             div.id = this.dragElId;
19328             var s  = div.style;
19329
19330             s.position   = "absolute";
19331             s.visibility = "hidden";
19332             s.cursor     = "move";
19333             s.border     = "2px solid #aaa";
19334             s.zIndex     = 999;
19335
19336             // appendChild can blow up IE if invoked prior to the window load event
19337             // while rendering a table.  It is possible there are other scenarios
19338             // that would cause this to happen as well.
19339             body.insertBefore(div, body.firstChild);
19340         }
19341     },
19342
19343     /**
19344      * Initialization for the drag frame element.  Must be called in the
19345      * constructor of all subclasses
19346      * @method initFrame
19347      */
19348     initFrame: function() {
19349         this.createFrame();
19350     },
19351
19352     applyConfig: function() {
19353         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19354
19355         this.resizeFrame = (this.config.resizeFrame !== false);
19356         this.centerFrame = (this.config.centerFrame);
19357         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19358     },
19359
19360     /**
19361      * Resizes the drag frame to the dimensions of the clicked object, positions
19362      * it over the object, and finally displays it
19363      * @method showFrame
19364      * @param {int} iPageX X click position
19365      * @param {int} iPageY Y click position
19366      * @private
19367      */
19368     showFrame: function(iPageX, iPageY) {
19369         var el = this.getEl();
19370         var dragEl = this.getDragEl();
19371         var s = dragEl.style;
19372
19373         this._resizeProxy();
19374
19375         if (this.centerFrame) {
19376             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19377                            Math.round(parseInt(s.height, 10)/2) );
19378         }
19379
19380         this.setDragElPos(iPageX, iPageY);
19381
19382         Roo.fly(dragEl).show();
19383     },
19384
19385     /**
19386      * The proxy is automatically resized to the dimensions of the linked
19387      * element when a drag is initiated, unless resizeFrame is set to false
19388      * @method _resizeProxy
19389      * @private
19390      */
19391     _resizeProxy: function() {
19392         if (this.resizeFrame) {
19393             var el = this.getEl();
19394             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19395         }
19396     },
19397
19398     // overrides Roo.dd.DragDrop
19399     b4MouseDown: function(e) {
19400         var x = e.getPageX();
19401         var y = e.getPageY();
19402         this.autoOffset(x, y);
19403         this.setDragElPos(x, y);
19404     },
19405
19406     // overrides Roo.dd.DragDrop
19407     b4StartDrag: function(x, y) {
19408         // show the drag frame
19409         this.showFrame(x, y);
19410     },
19411
19412     // overrides Roo.dd.DragDrop
19413     b4EndDrag: function(e) {
19414         Roo.fly(this.getDragEl()).hide();
19415     },
19416
19417     // overrides Roo.dd.DragDrop
19418     // By default we try to move the element to the last location of the frame.
19419     // This is so that the default behavior mirrors that of Roo.dd.DD.
19420     endDrag: function(e) {
19421
19422         var lel = this.getEl();
19423         var del = this.getDragEl();
19424
19425         // Show the drag frame briefly so we can get its position
19426         del.style.visibility = "";
19427
19428         this.beforeMove();
19429         // Hide the linked element before the move to get around a Safari
19430         // rendering bug.
19431         lel.style.visibility = "hidden";
19432         Roo.dd.DDM.moveToEl(lel, del);
19433         del.style.visibility = "hidden";
19434         lel.style.visibility = "";
19435
19436         this.afterDrag();
19437     },
19438
19439     beforeMove : function(){
19440
19441     },
19442
19443     afterDrag : function(){
19444
19445     },
19446
19447     toString: function() {
19448         return ("DDProxy " + this.id);
19449     }
19450
19451 });
19452 /*
19453  * Based on:
19454  * Ext JS Library 1.1.1
19455  * Copyright(c) 2006-2007, Ext JS, LLC.
19456  *
19457  * Originally Released Under LGPL - original licence link has changed is not relivant.
19458  *
19459  * Fork - LGPL
19460  * <script type="text/javascript">
19461  */
19462
19463  /**
19464  * @class Roo.dd.DDTarget
19465  * A DragDrop implementation that does not move, but can be a drop
19466  * target.  You would get the same result by simply omitting implementation
19467  * for the event callbacks, but this way we reduce the processing cost of the
19468  * event listener and the callbacks.
19469  * @extends Roo.dd.DragDrop
19470  * @constructor
19471  * @param {String} id the id of the element that is a drop target
19472  * @param {String} sGroup the group of related DragDrop objects
19473  * @param {object} config an object containing configurable attributes
19474  *                 Valid properties for DDTarget in addition to those in
19475  *                 DragDrop:
19476  *                    none
19477  */
19478 Roo.dd.DDTarget = function(id, sGroup, config) {
19479     if (id) {
19480         this.initTarget(id, sGroup, config);
19481     }
19482     if (config.listeners || config.events) { 
19483        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19484             listeners : config.listeners || {}, 
19485             events : config.events || {} 
19486         });    
19487     }
19488 };
19489
19490 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19491 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19492     toString: function() {
19493         return ("DDTarget " + this.id);
19494     }
19495 });
19496 /*
19497  * Based on:
19498  * Ext JS Library 1.1.1
19499  * Copyright(c) 2006-2007, Ext JS, LLC.
19500  *
19501  * Originally Released Under LGPL - original licence link has changed is not relivant.
19502  *
19503  * Fork - LGPL
19504  * <script type="text/javascript">
19505  */
19506  
19507
19508 /**
19509  * @class Roo.dd.ScrollManager
19510  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19511  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19512  * @singleton
19513  */
19514 Roo.dd.ScrollManager = function(){
19515     var ddm = Roo.dd.DragDropMgr;
19516     var els = {};
19517     var dragEl = null;
19518     var proc = {};
19519     
19520     
19521     
19522     var onStop = function(e){
19523         dragEl = null;
19524         clearProc();
19525     };
19526     
19527     var triggerRefresh = function(){
19528         if(ddm.dragCurrent){
19529              ddm.refreshCache(ddm.dragCurrent.groups);
19530         }
19531     };
19532     
19533     var doScroll = function(){
19534         if(ddm.dragCurrent){
19535             var dds = Roo.dd.ScrollManager;
19536             if(!dds.animate){
19537                 if(proc.el.scroll(proc.dir, dds.increment)){
19538                     triggerRefresh();
19539                 }
19540             }else{
19541                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19542             }
19543         }
19544     };
19545     
19546     var clearProc = function(){
19547         if(proc.id){
19548             clearInterval(proc.id);
19549         }
19550         proc.id = 0;
19551         proc.el = null;
19552         proc.dir = "";
19553     };
19554     
19555     var startProc = function(el, dir){
19556          Roo.log('scroll startproc');
19557         clearProc();
19558         proc.el = el;
19559         proc.dir = dir;
19560         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19561     };
19562     
19563     var onFire = function(e, isDrop){
19564        
19565         if(isDrop || !ddm.dragCurrent){ return; }
19566         var dds = Roo.dd.ScrollManager;
19567         if(!dragEl || dragEl != ddm.dragCurrent){
19568             dragEl = ddm.dragCurrent;
19569             // refresh regions on drag start
19570             dds.refreshCache();
19571         }
19572         
19573         var xy = Roo.lib.Event.getXY(e);
19574         var pt = new Roo.lib.Point(xy[0], xy[1]);
19575         for(var id in els){
19576             var el = els[id], r = el._region;
19577             if(r && r.contains(pt) && el.isScrollable()){
19578                 if(r.bottom - pt.y <= dds.thresh){
19579                     if(proc.el != el){
19580                         startProc(el, "down");
19581                     }
19582                     return;
19583                 }else if(r.right - pt.x <= dds.thresh){
19584                     if(proc.el != el){
19585                         startProc(el, "left");
19586                     }
19587                     return;
19588                 }else if(pt.y - r.top <= dds.thresh){
19589                     if(proc.el != el){
19590                         startProc(el, "up");
19591                     }
19592                     return;
19593                 }else if(pt.x - r.left <= dds.thresh){
19594                     if(proc.el != el){
19595                         startProc(el, "right");
19596                     }
19597                     return;
19598                 }
19599             }
19600         }
19601         clearProc();
19602     };
19603     
19604     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19605     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19606     
19607     return {
19608         /**
19609          * Registers new overflow element(s) to auto scroll
19610          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19611          */
19612         register : function(el){
19613             if(el instanceof Array){
19614                 for(var i = 0, len = el.length; i < len; i++) {
19615                         this.register(el[i]);
19616                 }
19617             }else{
19618                 el = Roo.get(el);
19619                 els[el.id] = el;
19620             }
19621             Roo.dd.ScrollManager.els = els;
19622         },
19623         
19624         /**
19625          * Unregisters overflow element(s) so they are no longer scrolled
19626          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19627          */
19628         unregister : function(el){
19629             if(el instanceof Array){
19630                 for(var i = 0, len = el.length; i < len; i++) {
19631                         this.unregister(el[i]);
19632                 }
19633             }else{
19634                 el = Roo.get(el);
19635                 delete els[el.id];
19636             }
19637         },
19638         
19639         /**
19640          * The number of pixels from the edge of a container the pointer needs to be to 
19641          * trigger scrolling (defaults to 25)
19642          * @type Number
19643          */
19644         thresh : 25,
19645         
19646         /**
19647          * The number of pixels to scroll in each scroll increment (defaults to 50)
19648          * @type Number
19649          */
19650         increment : 100,
19651         
19652         /**
19653          * The frequency of scrolls in milliseconds (defaults to 500)
19654          * @type Number
19655          */
19656         frequency : 500,
19657         
19658         /**
19659          * True to animate the scroll (defaults to true)
19660          * @type Boolean
19661          */
19662         animate: true,
19663         
19664         /**
19665          * The animation duration in seconds - 
19666          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19667          * @type Number
19668          */
19669         animDuration: .4,
19670         
19671         /**
19672          * Manually trigger a cache refresh.
19673          */
19674         refreshCache : function(){
19675             for(var id in els){
19676                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19677                     els[id]._region = els[id].getRegion();
19678                 }
19679             }
19680         }
19681     };
19682 }();/*
19683  * Based on:
19684  * Ext JS Library 1.1.1
19685  * Copyright(c) 2006-2007, Ext JS, LLC.
19686  *
19687  * Originally Released Under LGPL - original licence link has changed is not relivant.
19688  *
19689  * Fork - LGPL
19690  * <script type="text/javascript">
19691  */
19692  
19693
19694 /**
19695  * @class Roo.dd.Registry
19696  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19697  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19698  * @singleton
19699  */
19700 Roo.dd.Registry = function(){
19701     var elements = {}; 
19702     var handles = {}; 
19703     var autoIdSeed = 0;
19704
19705     var getId = function(el, autogen){
19706         if(typeof el == "string"){
19707             return el;
19708         }
19709         var id = el.id;
19710         if(!id && autogen !== false){
19711             id = "roodd-" + (++autoIdSeed);
19712             el.id = id;
19713         }
19714         return id;
19715     };
19716     
19717     return {
19718     /**
19719      * Register a drag drop element
19720      * @param {String|HTMLElement} element The id or DOM node to register
19721      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19722      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19723      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19724      * populated in the data object (if applicable):
19725      * <pre>
19726 Value      Description<br />
19727 ---------  ------------------------------------------<br />
19728 handles    Array of DOM nodes that trigger dragging<br />
19729            for the element being registered<br />
19730 isHandle   True if the element passed in triggers<br />
19731            dragging itself, else false
19732 </pre>
19733      */
19734         register : function(el, data){
19735             data = data || {};
19736             if(typeof el == "string"){
19737                 el = document.getElementById(el);
19738             }
19739             data.ddel = el;
19740             elements[getId(el)] = data;
19741             if(data.isHandle !== false){
19742                 handles[data.ddel.id] = data;
19743             }
19744             if(data.handles){
19745                 var hs = data.handles;
19746                 for(var i = 0, len = hs.length; i < len; i++){
19747                         handles[getId(hs[i])] = data;
19748                 }
19749             }
19750         },
19751
19752     /**
19753      * Unregister a drag drop element
19754      * @param {String|HTMLElement}  element The id or DOM node to unregister
19755      */
19756         unregister : function(el){
19757             var id = getId(el, false);
19758             var data = elements[id];
19759             if(data){
19760                 delete elements[id];
19761                 if(data.handles){
19762                     var hs = data.handles;
19763                     for(var i = 0, len = hs.length; i < len; i++){
19764                         delete handles[getId(hs[i], false)];
19765                     }
19766                 }
19767             }
19768         },
19769
19770     /**
19771      * Returns the handle registered for a DOM Node by id
19772      * @param {String|HTMLElement} id The DOM node or id to look up
19773      * @return {Object} handle The custom handle data
19774      */
19775         getHandle : function(id){
19776             if(typeof id != "string"){ // must be element?
19777                 id = id.id;
19778             }
19779             return handles[id];
19780         },
19781
19782     /**
19783      * Returns the handle that is registered for the DOM node that is the target of the event
19784      * @param {Event} e The event
19785      * @return {Object} handle The custom handle data
19786      */
19787         getHandleFromEvent : function(e){
19788             var t = Roo.lib.Event.getTarget(e);
19789             return t ? handles[t.id] : null;
19790         },
19791
19792     /**
19793      * Returns a custom data object that is registered for a DOM node by id
19794      * @param {String|HTMLElement} id The DOM node or id to look up
19795      * @return {Object} data The custom data
19796      */
19797         getTarget : function(id){
19798             if(typeof id != "string"){ // must be element?
19799                 id = id.id;
19800             }
19801             return elements[id];
19802         },
19803
19804     /**
19805      * Returns a custom data object that is registered for the DOM node that is the target of the event
19806      * @param {Event} e The event
19807      * @return {Object} data The custom data
19808      */
19809         getTargetFromEvent : function(e){
19810             var t = Roo.lib.Event.getTarget(e);
19811             return t ? elements[t.id] || handles[t.id] : null;
19812         }
19813     };
19814 }();/*
19815  * Based on:
19816  * Ext JS Library 1.1.1
19817  * Copyright(c) 2006-2007, Ext JS, LLC.
19818  *
19819  * Originally Released Under LGPL - original licence link has changed is not relivant.
19820  *
19821  * Fork - LGPL
19822  * <script type="text/javascript">
19823  */
19824  
19825
19826 /**
19827  * @class Roo.dd.StatusProxy
19828  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19829  * default drag proxy used by all Roo.dd components.
19830  * @constructor
19831  * @param {Object} config
19832  */
19833 Roo.dd.StatusProxy = function(config){
19834     Roo.apply(this, config);
19835     this.id = this.id || Roo.id();
19836     this.el = new Roo.Layer({
19837         dh: {
19838             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19839                 {tag: "div", cls: "x-dd-drop-icon"},
19840                 {tag: "div", cls: "x-dd-drag-ghost"}
19841             ]
19842         }, 
19843         shadow: !config || config.shadow !== false
19844     });
19845     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19846     this.dropStatus = this.dropNotAllowed;
19847 };
19848
19849 Roo.dd.StatusProxy.prototype = {
19850     /**
19851      * @cfg {String} dropAllowed
19852      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19853      */
19854     dropAllowed : "x-dd-drop-ok",
19855     /**
19856      * @cfg {String} dropNotAllowed
19857      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19858      */
19859     dropNotAllowed : "x-dd-drop-nodrop",
19860
19861     /**
19862      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19863      * over the current target element.
19864      * @param {String} cssClass The css class for the new drop status indicator image
19865      */
19866     setStatus : function(cssClass){
19867         cssClass = cssClass || this.dropNotAllowed;
19868         if(this.dropStatus != cssClass){
19869             this.el.replaceClass(this.dropStatus, cssClass);
19870             this.dropStatus = cssClass;
19871         }
19872     },
19873
19874     /**
19875      * Resets the status indicator to the default dropNotAllowed value
19876      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19877      */
19878     reset : function(clearGhost){
19879         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19880         this.dropStatus = this.dropNotAllowed;
19881         if(clearGhost){
19882             this.ghost.update("");
19883         }
19884     },
19885
19886     /**
19887      * Updates the contents of the ghost element
19888      * @param {String} html The html that will replace the current innerHTML of the ghost element
19889      */
19890     update : function(html){
19891         if(typeof html == "string"){
19892             this.ghost.update(html);
19893         }else{
19894             this.ghost.update("");
19895             html.style.margin = "0";
19896             this.ghost.dom.appendChild(html);
19897         }
19898         // ensure float = none set?? cant remember why though.
19899         var el = this.ghost.dom.firstChild;
19900                 if(el){
19901                         Roo.fly(el).setStyle('float', 'none');
19902                 }
19903     },
19904     
19905     /**
19906      * Returns the underlying proxy {@link Roo.Layer}
19907      * @return {Roo.Layer} el
19908     */
19909     getEl : function(){
19910         return this.el;
19911     },
19912
19913     /**
19914      * Returns the ghost element
19915      * @return {Roo.Element} el
19916      */
19917     getGhost : function(){
19918         return this.ghost;
19919     },
19920
19921     /**
19922      * Hides the proxy
19923      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19924      */
19925     hide : function(clear){
19926         this.el.hide();
19927         if(clear){
19928             this.reset(true);
19929         }
19930     },
19931
19932     /**
19933      * Stops the repair animation if it's currently running
19934      */
19935     stop : function(){
19936         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19937             this.anim.stop();
19938         }
19939     },
19940
19941     /**
19942      * Displays this proxy
19943      */
19944     show : function(){
19945         this.el.show();
19946     },
19947
19948     /**
19949      * Force the Layer to sync its shadow and shim positions to the element
19950      */
19951     sync : function(){
19952         this.el.sync();
19953     },
19954
19955     /**
19956      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19957      * invalid drop operation by the item being dragged.
19958      * @param {Array} xy The XY position of the element ([x, y])
19959      * @param {Function} callback The function to call after the repair is complete
19960      * @param {Object} scope The scope in which to execute the callback
19961      */
19962     repair : function(xy, callback, scope){
19963         this.callback = callback;
19964         this.scope = scope;
19965         if(xy && this.animRepair !== false){
19966             this.el.addClass("x-dd-drag-repair");
19967             this.el.hideUnders(true);
19968             this.anim = this.el.shift({
19969                 duration: this.repairDuration || .5,
19970                 easing: 'easeOut',
19971                 xy: xy,
19972                 stopFx: true,
19973                 callback: this.afterRepair,
19974                 scope: this
19975             });
19976         }else{
19977             this.afterRepair();
19978         }
19979     },
19980
19981     // private
19982     afterRepair : function(){
19983         this.hide(true);
19984         if(typeof this.callback == "function"){
19985             this.callback.call(this.scope || this);
19986         }
19987         this.callback = null;
19988         this.scope = null;
19989     }
19990 };/*
19991  * Based on:
19992  * Ext JS Library 1.1.1
19993  * Copyright(c) 2006-2007, Ext JS, LLC.
19994  *
19995  * Originally Released Under LGPL - original licence link has changed is not relivant.
19996  *
19997  * Fork - LGPL
19998  * <script type="text/javascript">
19999  */
20000
20001 /**
20002  * @class Roo.dd.DragSource
20003  * @extends Roo.dd.DDProxy
20004  * A simple class that provides the basic implementation needed to make any element draggable.
20005  * @constructor
20006  * @param {String/HTMLElement/Element} el The container element
20007  * @param {Object} config
20008  */
20009 Roo.dd.DragSource = function(el, config){
20010     this.el = Roo.get(el);
20011     this.dragData = {};
20012     
20013     Roo.apply(this, config);
20014     
20015     if(!this.proxy){
20016         this.proxy = new Roo.dd.StatusProxy();
20017     }
20018
20019     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20020           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20021     
20022     this.dragging = false;
20023 };
20024
20025 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20026     /**
20027      * @cfg {String} dropAllowed
20028      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20029      */
20030     dropAllowed : "x-dd-drop-ok",
20031     /**
20032      * @cfg {String} dropNotAllowed
20033      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20034      */
20035     dropNotAllowed : "x-dd-drop-nodrop",
20036
20037     /**
20038      * Returns the data object associated with this drag source
20039      * @return {Object} data An object containing arbitrary data
20040      */
20041     getDragData : function(e){
20042         return this.dragData;
20043     },
20044
20045     // private
20046     onDragEnter : function(e, id){
20047         var target = Roo.dd.DragDropMgr.getDDById(id);
20048         this.cachedTarget = target;
20049         if(this.beforeDragEnter(target, e, id) !== false){
20050             if(target.isNotifyTarget){
20051                 var status = target.notifyEnter(this, e, this.dragData);
20052                 this.proxy.setStatus(status);
20053             }else{
20054                 this.proxy.setStatus(this.dropAllowed);
20055             }
20056             
20057             if(this.afterDragEnter){
20058                 /**
20059                  * An empty function by default, but provided so that you can perform a custom action
20060                  * when the dragged item enters the drop target by providing an implementation.
20061                  * @param {Roo.dd.DragDrop} target The drop target
20062                  * @param {Event} e The event object
20063                  * @param {String} id The id of the dragged element
20064                  * @method afterDragEnter
20065                  */
20066                 this.afterDragEnter(target, e, id);
20067             }
20068         }
20069     },
20070
20071     /**
20072      * An empty function by default, but provided so that you can perform a custom action
20073      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20074      * @param {Roo.dd.DragDrop} target The drop target
20075      * @param {Event} e The event object
20076      * @param {String} id The id of the dragged element
20077      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20078      */
20079     beforeDragEnter : function(target, e, id){
20080         return true;
20081     },
20082
20083     // private
20084     alignElWithMouse: function() {
20085         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20086         this.proxy.sync();
20087     },
20088
20089     // private
20090     onDragOver : function(e, id){
20091         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20092         if(this.beforeDragOver(target, e, id) !== false){
20093             if(target.isNotifyTarget){
20094                 var status = target.notifyOver(this, e, this.dragData);
20095                 this.proxy.setStatus(status);
20096             }
20097
20098             if(this.afterDragOver){
20099                 /**
20100                  * An empty function by default, but provided so that you can perform a custom action
20101                  * while the dragged item is over the drop target by providing an implementation.
20102                  * @param {Roo.dd.DragDrop} target The drop target
20103                  * @param {Event} e The event object
20104                  * @param {String} id The id of the dragged element
20105                  * @method afterDragOver
20106                  */
20107                 this.afterDragOver(target, e, id);
20108             }
20109         }
20110     },
20111
20112     /**
20113      * An empty function by default, but provided so that you can perform a custom action
20114      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20115      * @param {Roo.dd.DragDrop} target The drop target
20116      * @param {Event} e The event object
20117      * @param {String} id The id of the dragged element
20118      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20119      */
20120     beforeDragOver : function(target, e, id){
20121         return true;
20122     },
20123
20124     // private
20125     onDragOut : function(e, id){
20126         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20127         if(this.beforeDragOut(target, e, id) !== false){
20128             if(target.isNotifyTarget){
20129                 target.notifyOut(this, e, this.dragData);
20130             }
20131             this.proxy.reset();
20132             if(this.afterDragOut){
20133                 /**
20134                  * An empty function by default, but provided so that you can perform a custom action
20135                  * after the dragged item is dragged out of the target without dropping.
20136                  * @param {Roo.dd.DragDrop} target The drop target
20137                  * @param {Event} e The event object
20138                  * @param {String} id The id of the dragged element
20139                  * @method afterDragOut
20140                  */
20141                 this.afterDragOut(target, e, id);
20142             }
20143         }
20144         this.cachedTarget = null;
20145     },
20146
20147     /**
20148      * An empty function by default, but provided so that you can perform a custom action before the dragged
20149      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20150      * @param {Roo.dd.DragDrop} target The drop target
20151      * @param {Event} e The event object
20152      * @param {String} id The id of the dragged element
20153      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20154      */
20155     beforeDragOut : function(target, e, id){
20156         return true;
20157     },
20158     
20159     // private
20160     onDragDrop : function(e, id){
20161         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20162         if(this.beforeDragDrop(target, e, id) !== false){
20163             if(target.isNotifyTarget){
20164                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20165                     this.onValidDrop(target, e, id);
20166                 }else{
20167                     this.onInvalidDrop(target, e, id);
20168                 }
20169             }else{
20170                 this.onValidDrop(target, e, id);
20171             }
20172             
20173             if(this.afterDragDrop){
20174                 /**
20175                  * An empty function by default, but provided so that you can perform a custom action
20176                  * after a valid drag drop has occurred by providing an implementation.
20177                  * @param {Roo.dd.DragDrop} target The drop target
20178                  * @param {Event} e The event object
20179                  * @param {String} id The id of the dropped element
20180                  * @method afterDragDrop
20181                  */
20182                 this.afterDragDrop(target, e, id);
20183             }
20184         }
20185         delete this.cachedTarget;
20186     },
20187
20188     /**
20189      * An empty function by default, but provided so that you can perform a custom action before the dragged
20190      * item is dropped onto the target and optionally cancel the onDragDrop.
20191      * @param {Roo.dd.DragDrop} target The drop target
20192      * @param {Event} e The event object
20193      * @param {String} id The id of the dragged element
20194      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20195      */
20196     beforeDragDrop : function(target, e, id){
20197         return true;
20198     },
20199
20200     // private
20201     onValidDrop : function(target, e, id){
20202         this.hideProxy();
20203         if(this.afterValidDrop){
20204             /**
20205              * An empty function by default, but provided so that you can perform a custom action
20206              * after a valid drop has occurred by providing an implementation.
20207              * @param {Object} target The target DD 
20208              * @param {Event} e The event object
20209              * @param {String} id The id of the dropped element
20210              * @method afterInvalidDrop
20211              */
20212             this.afterValidDrop(target, e, id);
20213         }
20214     },
20215
20216     // private
20217     getRepairXY : function(e, data){
20218         return this.el.getXY();  
20219     },
20220
20221     // private
20222     onInvalidDrop : function(target, e, id){
20223         this.beforeInvalidDrop(target, e, id);
20224         if(this.cachedTarget){
20225             if(this.cachedTarget.isNotifyTarget){
20226                 this.cachedTarget.notifyOut(this, e, this.dragData);
20227             }
20228             this.cacheTarget = null;
20229         }
20230         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20231
20232         if(this.afterInvalidDrop){
20233             /**
20234              * An empty function by default, but provided so that you can perform a custom action
20235              * after an invalid drop has occurred by providing an implementation.
20236              * @param {Event} e The event object
20237              * @param {String} id The id of the dropped element
20238              * @method afterInvalidDrop
20239              */
20240             this.afterInvalidDrop(e, id);
20241         }
20242     },
20243
20244     // private
20245     afterRepair : function(){
20246         if(Roo.enableFx){
20247             this.el.highlight(this.hlColor || "c3daf9");
20248         }
20249         this.dragging = false;
20250     },
20251
20252     /**
20253      * An empty function by default, but provided so that you can perform a custom action after an invalid
20254      * drop has occurred.
20255      * @param {Roo.dd.DragDrop} target The drop target
20256      * @param {Event} e The event object
20257      * @param {String} id The id of the dragged element
20258      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20259      */
20260     beforeInvalidDrop : function(target, e, id){
20261         return true;
20262     },
20263
20264     // private
20265     handleMouseDown : function(e){
20266         if(this.dragging) {
20267             return;
20268         }
20269         var data = this.getDragData(e);
20270         if(data && this.onBeforeDrag(data, e) !== false){
20271             this.dragData = data;
20272             this.proxy.stop();
20273             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20274         } 
20275     },
20276
20277     /**
20278      * An empty function by default, but provided so that you can perform a custom action before the initial
20279      * drag event begins and optionally cancel it.
20280      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20281      * @param {Event} e The event object
20282      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20283      */
20284     onBeforeDrag : function(data, e){
20285         return true;
20286     },
20287
20288     /**
20289      * An empty function by default, but provided so that you can perform a custom action once the initial
20290      * drag event has begun.  The drag cannot be canceled from this function.
20291      * @param {Number} x The x position of the click on the dragged object
20292      * @param {Number} y The y position of the click on the dragged object
20293      */
20294     onStartDrag : Roo.emptyFn,
20295
20296     // private - YUI override
20297     startDrag : function(x, y){
20298         this.proxy.reset();
20299         this.dragging = true;
20300         this.proxy.update("");
20301         this.onInitDrag(x, y);
20302         this.proxy.show();
20303     },
20304
20305     // private
20306     onInitDrag : function(x, y){
20307         var clone = this.el.dom.cloneNode(true);
20308         clone.id = Roo.id(); // prevent duplicate ids
20309         this.proxy.update(clone);
20310         this.onStartDrag(x, y);
20311         return true;
20312     },
20313
20314     /**
20315      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20316      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20317      */
20318     getProxy : function(){
20319         return this.proxy;  
20320     },
20321
20322     /**
20323      * Hides the drag source's {@link Roo.dd.StatusProxy}
20324      */
20325     hideProxy : function(){
20326         this.proxy.hide();  
20327         this.proxy.reset(true);
20328         this.dragging = false;
20329     },
20330
20331     // private
20332     triggerCacheRefresh : function(){
20333         Roo.dd.DDM.refreshCache(this.groups);
20334     },
20335
20336     // private - override to prevent hiding
20337     b4EndDrag: function(e) {
20338     },
20339
20340     // private - override to prevent moving
20341     endDrag : function(e){
20342         this.onEndDrag(this.dragData, e);
20343     },
20344
20345     // private
20346     onEndDrag : function(data, e){
20347     },
20348     
20349     // private - pin to cursor
20350     autoOffset : function(x, y) {
20351         this.setDelta(-12, -20);
20352     }    
20353 });/*
20354  * Based on:
20355  * Ext JS Library 1.1.1
20356  * Copyright(c) 2006-2007, Ext JS, LLC.
20357  *
20358  * Originally Released Under LGPL - original licence link has changed is not relivant.
20359  *
20360  * Fork - LGPL
20361  * <script type="text/javascript">
20362  */
20363
20364
20365 /**
20366  * @class Roo.dd.DropTarget
20367  * @extends Roo.dd.DDTarget
20368  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20369  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20370  * @constructor
20371  * @param {String/HTMLElement/Element} el The container element
20372  * @param {Object} config
20373  */
20374 Roo.dd.DropTarget = function(el, config){
20375     this.el = Roo.get(el);
20376     
20377     var listeners = false; ;
20378     if (config && config.listeners) {
20379         listeners= config.listeners;
20380         delete config.listeners;
20381     }
20382     Roo.apply(this, config);
20383     
20384     if(this.containerScroll){
20385         Roo.dd.ScrollManager.register(this.el);
20386     }
20387     this.addEvents( {
20388          /**
20389          * @scope Roo.dd.DropTarget
20390          */
20391          
20392          /**
20393          * @event enter
20394          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20395          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20396          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20397          * 
20398          * IMPORTANT : it should set this.overClass and this.dropAllowed
20399          * 
20400          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20401          * @param {Event} e The event
20402          * @param {Object} data An object containing arbitrary data supplied by the drag source
20403          */
20404         "enter" : true,
20405         
20406          /**
20407          * @event over
20408          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20409          * This method will be called on every mouse movement while the drag source is over the drop target.
20410          * This default implementation simply returns the dropAllowed config value.
20411          * 
20412          * IMPORTANT : it should set this.dropAllowed
20413          * 
20414          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20415          * @param {Event} e The event
20416          * @param {Object} data An object containing arbitrary data supplied by the drag source
20417          
20418          */
20419         "over" : true,
20420         /**
20421          * @event out
20422          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20423          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20424          * overClass (if any) from the drop element.
20425          * 
20426          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20427          * @param {Event} e The event
20428          * @param {Object} data An object containing arbitrary data supplied by the drag source
20429          */
20430          "out" : true,
20431          
20432         /**
20433          * @event drop
20434          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20435          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20436          * implementation that does something to process the drop event and returns true so that the drag source's
20437          * repair action does not run.
20438          * 
20439          * IMPORTANT : it should set this.success
20440          * 
20441          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20442          * @param {Event} e The event
20443          * @param {Object} data An object containing arbitrary data supplied by the drag source
20444         */
20445          "drop" : true
20446     });
20447             
20448      
20449     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20450         this.el.dom, 
20451         this.ddGroup || this.group,
20452         {
20453             isTarget: true,
20454             listeners : listeners || {} 
20455            
20456         
20457         }
20458     );
20459
20460 };
20461
20462 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20463     /**
20464      * @cfg {String} overClass
20465      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20466      */
20467      /**
20468      * @cfg {String} ddGroup
20469      * The drag drop group to handle drop events for
20470      */
20471      
20472     /**
20473      * @cfg {String} dropAllowed
20474      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20475      */
20476     dropAllowed : "x-dd-drop-ok",
20477     /**
20478      * @cfg {String} dropNotAllowed
20479      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20480      */
20481     dropNotAllowed : "x-dd-drop-nodrop",
20482     /**
20483      * @cfg {boolean} success
20484      * set this after drop listener.. 
20485      */
20486     success : false,
20487     /**
20488      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20489      * if the drop point is valid for over/enter..
20490      */
20491     valid : false,
20492     // private
20493     isTarget : true,
20494
20495     // private
20496     isNotifyTarget : true,
20497     
20498     /**
20499      * @hide
20500      */
20501     notifyEnter : function(dd, e, data)
20502     {
20503         this.valid = true;
20504         this.fireEvent('enter', dd, e, data);
20505         if(this.overClass){
20506             this.el.addClass(this.overClass);
20507         }
20508         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20509             this.valid ? this.dropAllowed : this.dropNotAllowed
20510         );
20511     },
20512
20513     /**
20514      * @hide
20515      */
20516     notifyOver : function(dd, e, data)
20517     {
20518         this.valid = true;
20519         this.fireEvent('over', dd, e, data);
20520         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20521             this.valid ? this.dropAllowed : this.dropNotAllowed
20522         );
20523     },
20524
20525     /**
20526      * @hide
20527      */
20528     notifyOut : function(dd, e, data)
20529     {
20530         this.fireEvent('out', dd, e, data);
20531         if(this.overClass){
20532             this.el.removeClass(this.overClass);
20533         }
20534     },
20535
20536     /**
20537      * @hide
20538      */
20539     notifyDrop : function(dd, e, data)
20540     {
20541         this.success = false;
20542         this.fireEvent('drop', dd, e, data);
20543         return this.success;
20544     }
20545 });/*
20546  * Based on:
20547  * Ext JS Library 1.1.1
20548  * Copyright(c) 2006-2007, Ext JS, LLC.
20549  *
20550  * Originally Released Under LGPL - original licence link has changed is not relivant.
20551  *
20552  * Fork - LGPL
20553  * <script type="text/javascript">
20554  */
20555
20556
20557 /**
20558  * @class Roo.dd.DragZone
20559  * @extends Roo.dd.DragSource
20560  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20561  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20562  * @constructor
20563  * @param {String/HTMLElement/Element} el The container element
20564  * @param {Object} config
20565  */
20566 Roo.dd.DragZone = function(el, config){
20567     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20568     if(this.containerScroll){
20569         Roo.dd.ScrollManager.register(this.el);
20570     }
20571 };
20572
20573 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20574     /**
20575      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20576      * for auto scrolling during drag operations.
20577      */
20578     /**
20579      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20580      * method after a failed drop (defaults to "c3daf9" - light blue)
20581      */
20582
20583     /**
20584      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20585      * for a valid target to drag based on the mouse down. Override this method
20586      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20587      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20588      * @param {EventObject} e The mouse down event
20589      * @return {Object} The dragData
20590      */
20591     getDragData : function(e){
20592         return Roo.dd.Registry.getHandleFromEvent(e);
20593     },
20594     
20595     /**
20596      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20597      * this.dragData.ddel
20598      * @param {Number} x The x position of the click on the dragged object
20599      * @param {Number} y The y position of the click on the dragged object
20600      * @return {Boolean} true to continue the drag, false to cancel
20601      */
20602     onInitDrag : function(x, y){
20603         this.proxy.update(this.dragData.ddel.cloneNode(true));
20604         this.onStartDrag(x, y);
20605         return true;
20606     },
20607     
20608     /**
20609      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20610      */
20611     afterRepair : function(){
20612         if(Roo.enableFx){
20613             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20614         }
20615         this.dragging = false;
20616     },
20617
20618     /**
20619      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20620      * the XY of this.dragData.ddel
20621      * @param {EventObject} e The mouse up event
20622      * @return {Array} The xy location (e.g. [100, 200])
20623      */
20624     getRepairXY : function(e){
20625         return Roo.Element.fly(this.dragData.ddel).getXY();  
20626     }
20627 });/*
20628  * Based on:
20629  * Ext JS Library 1.1.1
20630  * Copyright(c) 2006-2007, Ext JS, LLC.
20631  *
20632  * Originally Released Under LGPL - original licence link has changed is not relivant.
20633  *
20634  * Fork - LGPL
20635  * <script type="text/javascript">
20636  */
20637 /**
20638  * @class Roo.dd.DropZone
20639  * @extends Roo.dd.DropTarget
20640  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20641  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20642  * @constructor
20643  * @param {String/HTMLElement/Element} el The container element
20644  * @param {Object} config
20645  */
20646 Roo.dd.DropZone = function(el, config){
20647     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20648 };
20649
20650 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20651     /**
20652      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20653      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20654      * provide your own custom lookup.
20655      * @param {Event} e The event
20656      * @return {Object} data The custom data
20657      */
20658     getTargetFromEvent : function(e){
20659         return Roo.dd.Registry.getTargetFromEvent(e);
20660     },
20661
20662     /**
20663      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20664      * that it has registered.  This method has no default implementation and should be overridden to provide
20665      * node-specific processing if necessary.
20666      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20667      * {@link #getTargetFromEvent} for this node)
20668      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20669      * @param {Event} e The event
20670      * @param {Object} data An object containing arbitrary data supplied by the drag source
20671      */
20672     onNodeEnter : function(n, dd, e, data){
20673         
20674     },
20675
20676     /**
20677      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20678      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20679      * overridden to provide the proper feedback.
20680      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20681      * {@link #getTargetFromEvent} for this node)
20682      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20683      * @param {Event} e The event
20684      * @param {Object} data An object containing arbitrary data supplied by the drag source
20685      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20686      * underlying {@link Roo.dd.StatusProxy} can be updated
20687      */
20688     onNodeOver : function(n, dd, e, data){
20689         return this.dropAllowed;
20690     },
20691
20692     /**
20693      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20694      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20695      * node-specific processing if necessary.
20696      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20697      * {@link #getTargetFromEvent} for this node)
20698      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20699      * @param {Event} e The event
20700      * @param {Object} data An object containing arbitrary data supplied by the drag source
20701      */
20702     onNodeOut : function(n, dd, e, data){
20703         
20704     },
20705
20706     /**
20707      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20708      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20709      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20710      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20711      * {@link #getTargetFromEvent} for this node)
20712      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20713      * @param {Event} e The event
20714      * @param {Object} data An object containing arbitrary data supplied by the drag source
20715      * @return {Boolean} True if the drop was valid, else false
20716      */
20717     onNodeDrop : function(n, dd, e, data){
20718         return false;
20719     },
20720
20721     /**
20722      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20723      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20724      * it should be overridden to provide the proper feedback if necessary.
20725      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20726      * @param {Event} e The event
20727      * @param {Object} data An object containing arbitrary data supplied by the drag source
20728      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20729      * underlying {@link Roo.dd.StatusProxy} can be updated
20730      */
20731     onContainerOver : function(dd, e, data){
20732         return this.dropNotAllowed;
20733     },
20734
20735     /**
20736      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20737      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20738      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20739      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20740      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20741      * @param {Event} e The event
20742      * @param {Object} data An object containing arbitrary data supplied by the drag source
20743      * @return {Boolean} True if the drop was valid, else false
20744      */
20745     onContainerDrop : function(dd, e, data){
20746         return false;
20747     },
20748
20749     /**
20750      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20751      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20752      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20753      * you should override this method and provide a custom implementation.
20754      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20755      * @param {Event} e The event
20756      * @param {Object} data An object containing arbitrary data supplied by the drag source
20757      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20758      * underlying {@link Roo.dd.StatusProxy} can be updated
20759      */
20760     notifyEnter : function(dd, e, data){
20761         return this.dropNotAllowed;
20762     },
20763
20764     /**
20765      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20766      * This method will be called on every mouse movement while the drag source is over the drop zone.
20767      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20768      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20769      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20770      * registered node, it will call {@link #onContainerOver}.
20771      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20772      * @param {Event} e The event
20773      * @param {Object} data An object containing arbitrary data supplied by the drag source
20774      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20775      * underlying {@link Roo.dd.StatusProxy} can be updated
20776      */
20777     notifyOver : function(dd, e, data){
20778         var n = this.getTargetFromEvent(e);
20779         if(!n){ // not over valid drop target
20780             if(this.lastOverNode){
20781                 this.onNodeOut(this.lastOverNode, dd, e, data);
20782                 this.lastOverNode = null;
20783             }
20784             return this.onContainerOver(dd, e, data);
20785         }
20786         if(this.lastOverNode != n){
20787             if(this.lastOverNode){
20788                 this.onNodeOut(this.lastOverNode, dd, e, data);
20789             }
20790             this.onNodeEnter(n, dd, e, data);
20791             this.lastOverNode = n;
20792         }
20793         return this.onNodeOver(n, dd, e, data);
20794     },
20795
20796     /**
20797      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20798      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20799      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20800      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20801      * @param {Event} e The event
20802      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20803      */
20804     notifyOut : function(dd, e, data){
20805         if(this.lastOverNode){
20806             this.onNodeOut(this.lastOverNode, dd, e, data);
20807             this.lastOverNode = null;
20808         }
20809     },
20810
20811     /**
20812      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20813      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20814      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20815      * otherwise it will call {@link #onContainerDrop}.
20816      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20817      * @param {Event} e The event
20818      * @param {Object} data An object containing arbitrary data supplied by the drag source
20819      * @return {Boolean} True if the drop was valid, else false
20820      */
20821     notifyDrop : function(dd, e, data){
20822         if(this.lastOverNode){
20823             this.onNodeOut(this.lastOverNode, dd, e, data);
20824             this.lastOverNode = null;
20825         }
20826         var n = this.getTargetFromEvent(e);
20827         return n ?
20828             this.onNodeDrop(n, dd, e, data) :
20829             this.onContainerDrop(dd, e, data);
20830     },
20831
20832     // private
20833     triggerCacheRefresh : function(){
20834         Roo.dd.DDM.refreshCache(this.groups);
20835     }  
20836 });/*
20837  * Based on:
20838  * Ext JS Library 1.1.1
20839  * Copyright(c) 2006-2007, Ext JS, LLC.
20840  *
20841  * Originally Released Under LGPL - original licence link has changed is not relivant.
20842  *
20843  * Fork - LGPL
20844  * <script type="text/javascript">
20845  */
20846
20847
20848 /**
20849  * @class Roo.data.SortTypes
20850  * @singleton
20851  * Defines the default sorting (casting?) comparison functions used when sorting data.
20852  */
20853 Roo.data.SortTypes = {
20854     /**
20855      * Default sort that does nothing
20856      * @param {Mixed} s The value being converted
20857      * @return {Mixed} The comparison value
20858      */
20859     none : function(s){
20860         return s;
20861     },
20862     
20863     /**
20864      * The regular expression used to strip tags
20865      * @type {RegExp}
20866      * @property
20867      */
20868     stripTagsRE : /<\/?[^>]+>/gi,
20869     
20870     /**
20871      * Strips all HTML tags to sort on text only
20872      * @param {Mixed} s The value being converted
20873      * @return {String} The comparison value
20874      */
20875     asText : function(s){
20876         return String(s).replace(this.stripTagsRE, "");
20877     },
20878     
20879     /**
20880      * Strips all HTML tags to sort on text only - Case insensitive
20881      * @param {Mixed} s The value being converted
20882      * @return {String} The comparison value
20883      */
20884     asUCText : function(s){
20885         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20886     },
20887     
20888     /**
20889      * Case insensitive string
20890      * @param {Mixed} s The value being converted
20891      * @return {String} The comparison value
20892      */
20893     asUCString : function(s) {
20894         return String(s).toUpperCase();
20895     },
20896     
20897     /**
20898      * Date sorting
20899      * @param {Mixed} s The value being converted
20900      * @return {Number} The comparison value
20901      */
20902     asDate : function(s) {
20903         if(!s){
20904             return 0;
20905         }
20906         if(s instanceof Date){
20907             return s.getTime();
20908         }
20909         return Date.parse(String(s));
20910     },
20911     
20912     /**
20913      * Float sorting
20914      * @param {Mixed} s The value being converted
20915      * @return {Float} The comparison value
20916      */
20917     asFloat : function(s) {
20918         var val = parseFloat(String(s).replace(/,/g, ""));
20919         if(isNaN(val)) val = 0;
20920         return val;
20921     },
20922     
20923     /**
20924      * Integer sorting
20925      * @param {Mixed} s The value being converted
20926      * @return {Number} The comparison value
20927      */
20928     asInt : function(s) {
20929         var val = parseInt(String(s).replace(/,/g, ""));
20930         if(isNaN(val)) val = 0;
20931         return val;
20932     }
20933 };/*
20934  * Based on:
20935  * Ext JS Library 1.1.1
20936  * Copyright(c) 2006-2007, Ext JS, LLC.
20937  *
20938  * Originally Released Under LGPL - original licence link has changed is not relivant.
20939  *
20940  * Fork - LGPL
20941  * <script type="text/javascript">
20942  */
20943
20944 /**
20945 * @class Roo.data.Record
20946  * Instances of this class encapsulate both record <em>definition</em> information, and record
20947  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20948  * to access Records cached in an {@link Roo.data.Store} object.<br>
20949  * <p>
20950  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20951  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20952  * objects.<br>
20953  * <p>
20954  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20955  * @constructor
20956  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20957  * {@link #create}. The parameters are the same.
20958  * @param {Array} data An associative Array of data values keyed by the field name.
20959  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20960  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20961  * not specified an integer id is generated.
20962  */
20963 Roo.data.Record = function(data, id){
20964     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20965     this.data = data;
20966 };
20967
20968 /**
20969  * Generate a constructor for a specific record layout.
20970  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20971  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20972  * Each field definition object may contain the following properties: <ul>
20973  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
20974  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20975  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20976  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20977  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20978  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20979  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20980  * this may be omitted.</p></li>
20981  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20982  * <ul><li>auto (Default, implies no conversion)</li>
20983  * <li>string</li>
20984  * <li>int</li>
20985  * <li>float</li>
20986  * <li>boolean</li>
20987  * <li>date</li></ul></p></li>
20988  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20989  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20990  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20991  * by the Reader into an object that will be stored in the Record. It is passed the
20992  * following parameters:<ul>
20993  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20994  * </ul></p></li>
20995  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20996  * </ul>
20997  * <br>usage:<br><pre><code>
20998 var TopicRecord = Roo.data.Record.create(
20999     {name: 'title', mapping: 'topic_title'},
21000     {name: 'author', mapping: 'username'},
21001     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
21002     {name: 'lastPost', mapping: 'post_time', type: 'date'},
21003     {name: 'lastPoster', mapping: 'user2'},
21004     {name: 'excerpt', mapping: 'post_text'}
21005 );
21006
21007 var myNewRecord = new TopicRecord({
21008     title: 'Do my job please',
21009     author: 'noobie',
21010     totalPosts: 1,
21011     lastPost: new Date(),
21012     lastPoster: 'Animal',
21013     excerpt: 'No way dude!'
21014 });
21015 myStore.add(myNewRecord);
21016 </code></pre>
21017  * @method create
21018  * @static
21019  */
21020 Roo.data.Record.create = function(o){
21021     var f = function(){
21022         f.superclass.constructor.apply(this, arguments);
21023     };
21024     Roo.extend(f, Roo.data.Record);
21025     var p = f.prototype;
21026     p.fields = new Roo.util.MixedCollection(false, function(field){
21027         return field.name;
21028     });
21029     for(var i = 0, len = o.length; i < len; i++){
21030         p.fields.add(new Roo.data.Field(o[i]));
21031     }
21032     f.getField = function(name){
21033         return p.fields.get(name);  
21034     };
21035     return f;
21036 };
21037
21038 Roo.data.Record.AUTO_ID = 1000;
21039 Roo.data.Record.EDIT = 'edit';
21040 Roo.data.Record.REJECT = 'reject';
21041 Roo.data.Record.COMMIT = 'commit';
21042
21043 Roo.data.Record.prototype = {
21044     /**
21045      * Readonly flag - true if this record has been modified.
21046      * @type Boolean
21047      */
21048     dirty : false,
21049     editing : false,
21050     error: null,
21051     modified: null,
21052
21053     // private
21054     join : function(store){
21055         this.store = store;
21056     },
21057
21058     /**
21059      * Set the named field to the specified value.
21060      * @param {String} name The name of the field to set.
21061      * @param {Object} value The value to set the field to.
21062      */
21063     set : function(name, value){
21064         if(this.data[name] == value){
21065             return;
21066         }
21067         this.dirty = true;
21068         if(!this.modified){
21069             this.modified = {};
21070         }
21071         if(typeof this.modified[name] == 'undefined'){
21072             this.modified[name] = this.data[name];
21073         }
21074         this.data[name] = value;
21075         if(!this.editing && this.store){
21076             this.store.afterEdit(this);
21077         }       
21078     },
21079
21080     /**
21081      * Get the value of the named field.
21082      * @param {String} name The name of the field to get the value of.
21083      * @return {Object} The value of the field.
21084      */
21085     get : function(name){
21086         return this.data[name]; 
21087     },
21088
21089     // private
21090     beginEdit : function(){
21091         this.editing = true;
21092         this.modified = {}; 
21093     },
21094
21095     // private
21096     cancelEdit : function(){
21097         this.editing = false;
21098         delete this.modified;
21099     },
21100
21101     // private
21102     endEdit : function(){
21103         this.editing = false;
21104         if(this.dirty && this.store){
21105             this.store.afterEdit(this);
21106         }
21107     },
21108
21109     /**
21110      * Usually called by the {@link Roo.data.Store} which owns the Record.
21111      * Rejects all changes made to the Record since either creation, or the last commit operation.
21112      * Modified fields are reverted to their original values.
21113      * <p>
21114      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21115      * of reject operations.
21116      */
21117     reject : function(){
21118         var m = this.modified;
21119         for(var n in m){
21120             if(typeof m[n] != "function"){
21121                 this.data[n] = m[n];
21122             }
21123         }
21124         this.dirty = false;
21125         delete this.modified;
21126         this.editing = false;
21127         if(this.store){
21128             this.store.afterReject(this);
21129         }
21130     },
21131
21132     /**
21133      * Usually called by the {@link Roo.data.Store} which owns the Record.
21134      * Commits all changes made to the Record since either creation, or the last commit operation.
21135      * <p>
21136      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21137      * of commit operations.
21138      */
21139     commit : function(){
21140         this.dirty = false;
21141         delete this.modified;
21142         this.editing = false;
21143         if(this.store){
21144             this.store.afterCommit(this);
21145         }
21146     },
21147
21148     // private
21149     hasError : function(){
21150         return this.error != null;
21151     },
21152
21153     // private
21154     clearError : function(){
21155         this.error = null;
21156     },
21157
21158     /**
21159      * Creates a copy of this record.
21160      * @param {String} id (optional) A new record id if you don't want to use this record's id
21161      * @return {Record}
21162      */
21163     copy : function(newId) {
21164         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21165     }
21166 };/*
21167  * Based on:
21168  * Ext JS Library 1.1.1
21169  * Copyright(c) 2006-2007, Ext JS, LLC.
21170  *
21171  * Originally Released Under LGPL - original licence link has changed is not relivant.
21172  *
21173  * Fork - LGPL
21174  * <script type="text/javascript">
21175  */
21176
21177
21178
21179 /**
21180  * @class Roo.data.Store
21181  * @extends Roo.util.Observable
21182  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21183  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21184  * <p>
21185  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
21186  * has no knowledge of the format of the data returned by the Proxy.<br>
21187  * <p>
21188  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21189  * instances from the data object. These records are cached and made available through accessor functions.
21190  * @constructor
21191  * Creates a new Store.
21192  * @param {Object} config A config object containing the objects needed for the Store to access data,
21193  * and read the data into Records.
21194  */
21195 Roo.data.Store = function(config){
21196     this.data = new Roo.util.MixedCollection(false);
21197     this.data.getKey = function(o){
21198         return o.id;
21199     };
21200     this.baseParams = {};
21201     // private
21202     this.paramNames = {
21203         "start" : "start",
21204         "limit" : "limit",
21205         "sort" : "sort",
21206         "dir" : "dir",
21207         "multisort" : "_multisort"
21208     };
21209
21210     if(config && config.data){
21211         this.inlineData = config.data;
21212         delete config.data;
21213     }
21214
21215     Roo.apply(this, config);
21216     
21217     if(this.reader){ // reader passed
21218         this.reader = Roo.factory(this.reader, Roo.data);
21219         this.reader.xmodule = this.xmodule || false;
21220         if(!this.recordType){
21221             this.recordType = this.reader.recordType;
21222         }
21223         if(this.reader.onMetaChange){
21224             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21225         }
21226     }
21227
21228     if(this.recordType){
21229         this.fields = this.recordType.prototype.fields;
21230     }
21231     this.modified = [];
21232
21233     this.addEvents({
21234         /**
21235          * @event datachanged
21236          * Fires when the data cache has changed, and a widget which is using this Store
21237          * as a Record cache should refresh its view.
21238          * @param {Store} this
21239          */
21240         datachanged : true,
21241         /**
21242          * @event metachange
21243          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21244          * @param {Store} this
21245          * @param {Object} meta The JSON metadata
21246          */
21247         metachange : true,
21248         /**
21249          * @event add
21250          * Fires when Records have been added to the Store
21251          * @param {Store} this
21252          * @param {Roo.data.Record[]} records The array of Records added
21253          * @param {Number} index The index at which the record(s) were added
21254          */
21255         add : true,
21256         /**
21257          * @event remove
21258          * Fires when a Record has been removed from the Store
21259          * @param {Store} this
21260          * @param {Roo.data.Record} record The Record that was removed
21261          * @param {Number} index The index at which the record was removed
21262          */
21263         remove : true,
21264         /**
21265          * @event update
21266          * Fires when a Record has been updated
21267          * @param {Store} this
21268          * @param {Roo.data.Record} record The Record that was updated
21269          * @param {String} operation The update operation being performed.  Value may be one of:
21270          * <pre><code>
21271  Roo.data.Record.EDIT
21272  Roo.data.Record.REJECT
21273  Roo.data.Record.COMMIT
21274          * </code></pre>
21275          */
21276         update : true,
21277         /**
21278          * @event clear
21279          * Fires when the data cache has been cleared.
21280          * @param {Store} this
21281          */
21282         clear : true,
21283         /**
21284          * @event beforeload
21285          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21286          * the load action will be canceled.
21287          * @param {Store} this
21288          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21289          */
21290         beforeload : true,
21291         /**
21292          * @event beforeloadadd
21293          * Fires after a new set of Records has been loaded.
21294          * @param {Store} this
21295          * @param {Roo.data.Record[]} records The Records that were loaded
21296          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21297          */
21298         beforeloadadd : true,
21299         /**
21300          * @event load
21301          * Fires after a new set of Records has been loaded, before they are added to the store.
21302          * @param {Store} this
21303          * @param {Roo.data.Record[]} records The Records that were loaded
21304          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21305          * @params {Object} return from reader
21306          */
21307         load : true,
21308         /**
21309          * @event loadexception
21310          * Fires if an exception occurs in the Proxy during loading.
21311          * Called with the signature of the Proxy's "loadexception" event.
21312          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21313          * 
21314          * @param {Proxy} 
21315          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21316          * @param {Object} load options 
21317          * @param {Object} jsonData from your request (normally this contains the Exception)
21318          */
21319         loadexception : true
21320     });
21321     
21322     if(this.proxy){
21323         this.proxy = Roo.factory(this.proxy, Roo.data);
21324         this.proxy.xmodule = this.xmodule || false;
21325         this.relayEvents(this.proxy,  ["loadexception"]);
21326     }
21327     this.sortToggle = {};
21328     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21329
21330     Roo.data.Store.superclass.constructor.call(this);
21331
21332     if(this.inlineData){
21333         this.loadData(this.inlineData);
21334         delete this.inlineData;
21335     }
21336 };
21337
21338 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21339      /**
21340     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21341     * without a remote query - used by combo/forms at present.
21342     */
21343     
21344     /**
21345     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21346     */
21347     /**
21348     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21349     */
21350     /**
21351     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21352     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21353     */
21354     /**
21355     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21356     * on any HTTP request
21357     */
21358     /**
21359     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21360     */
21361     /**
21362     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21363     */
21364     multiSort: false,
21365     /**
21366     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21367     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21368     */
21369     remoteSort : false,
21370
21371     /**
21372     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21373      * loaded or when a record is removed. (defaults to false).
21374     */
21375     pruneModifiedRecords : false,
21376
21377     // private
21378     lastOptions : null,
21379
21380     /**
21381      * Add Records to the Store and fires the add event.
21382      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21383      */
21384     add : function(records){
21385         records = [].concat(records);
21386         for(var i = 0, len = records.length; i < len; i++){
21387             records[i].join(this);
21388         }
21389         var index = this.data.length;
21390         this.data.addAll(records);
21391         this.fireEvent("add", this, records, index);
21392     },
21393
21394     /**
21395      * Remove a Record from the Store and fires the remove event.
21396      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21397      */
21398     remove : function(record){
21399         var index = this.data.indexOf(record);
21400         this.data.removeAt(index);
21401         if(this.pruneModifiedRecords){
21402             this.modified.remove(record);
21403         }
21404         this.fireEvent("remove", this, record, index);
21405     },
21406
21407     /**
21408      * Remove all Records from the Store and fires the clear event.
21409      */
21410     removeAll : function(){
21411         this.data.clear();
21412         if(this.pruneModifiedRecords){
21413             this.modified = [];
21414         }
21415         this.fireEvent("clear", this);
21416     },
21417
21418     /**
21419      * Inserts Records to the Store at the given index and fires the add event.
21420      * @param {Number} index The start index at which to insert the passed Records.
21421      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21422      */
21423     insert : function(index, records){
21424         records = [].concat(records);
21425         for(var i = 0, len = records.length; i < len; i++){
21426             this.data.insert(index, records[i]);
21427             records[i].join(this);
21428         }
21429         this.fireEvent("add", this, records, index);
21430     },
21431
21432     /**
21433      * Get the index within the cache of the passed Record.
21434      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21435      * @return {Number} The index of the passed Record. Returns -1 if not found.
21436      */
21437     indexOf : function(record){
21438         return this.data.indexOf(record);
21439     },
21440
21441     /**
21442      * Get the index within the cache of the Record with the passed id.
21443      * @param {String} id The id of the Record to find.
21444      * @return {Number} The index of the Record. Returns -1 if not found.
21445      */
21446     indexOfId : function(id){
21447         return this.data.indexOfKey(id);
21448     },
21449
21450     /**
21451      * Get the Record with the specified id.
21452      * @param {String} id The id of the Record to find.
21453      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21454      */
21455     getById : function(id){
21456         return this.data.key(id);
21457     },
21458
21459     /**
21460      * Get the Record at the specified index.
21461      * @param {Number} index The index of the Record to find.
21462      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21463      */
21464     getAt : function(index){
21465         return this.data.itemAt(index);
21466     },
21467
21468     /**
21469      * Returns a range of Records between specified indices.
21470      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21471      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21472      * @return {Roo.data.Record[]} An array of Records
21473      */
21474     getRange : function(start, end){
21475         return this.data.getRange(start, end);
21476     },
21477
21478     // private
21479     storeOptions : function(o){
21480         o = Roo.apply({}, o);
21481         delete o.callback;
21482         delete o.scope;
21483         this.lastOptions = o;
21484     },
21485
21486     /**
21487      * Loads the Record cache from the configured Proxy using the configured Reader.
21488      * <p>
21489      * If using remote paging, then the first load call must specify the <em>start</em>
21490      * and <em>limit</em> properties in the options.params property to establish the initial
21491      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21492      * <p>
21493      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21494      * and this call will return before the new data has been loaded. Perform any post-processing
21495      * in a callback function, or in a "load" event handler.</strong>
21496      * <p>
21497      * @param {Object} options An object containing properties which control loading options:<ul>
21498      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21499      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21500      * passed the following arguments:<ul>
21501      * <li>r : Roo.data.Record[]</li>
21502      * <li>options: Options object from the load call</li>
21503      * <li>success: Boolean success indicator</li></ul></li>
21504      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21505      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21506      * </ul>
21507      */
21508     load : function(options){
21509         options = options || {};
21510         if(this.fireEvent("beforeload", this, options) !== false){
21511             this.storeOptions(options);
21512             var p = Roo.apply(options.params || {}, this.baseParams);
21513             // if meta was not loaded from remote source.. try requesting it.
21514             if (!this.reader.metaFromRemote) {
21515                 p._requestMeta = 1;
21516             }
21517             if(this.sortInfo && this.remoteSort){
21518                 var pn = this.paramNames;
21519                 p[pn["sort"]] = this.sortInfo.field;
21520                 p[pn["dir"]] = this.sortInfo.direction;
21521             }
21522             if (this.multiSort) {
21523                 var pn = this.paramNames;
21524                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21525             }
21526             
21527             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21528         }
21529     },
21530
21531     /**
21532      * Reloads the Record cache from the configured Proxy using the configured Reader and
21533      * the options from the last load operation performed.
21534      * @param {Object} options (optional) An object containing properties which may override the options
21535      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21536      * the most recently used options are reused).
21537      */
21538     reload : function(options){
21539         this.load(Roo.applyIf(options||{}, this.lastOptions));
21540     },
21541
21542     // private
21543     // Called as a callback by the Reader during a load operation.
21544     loadRecords : function(o, options, success){
21545         if(!o || success === false){
21546             if(success !== false){
21547                 this.fireEvent("load", this, [], options, o);
21548             }
21549             if(options.callback){
21550                 options.callback.call(options.scope || this, [], options, false);
21551             }
21552             return;
21553         }
21554         // if data returned failure - throw an exception.
21555         if (o.success === false) {
21556             // show a message if no listener is registered.
21557             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21558                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21559             }
21560             // loadmask wil be hooked into this..
21561             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21562             return;
21563         }
21564         var r = o.records, t = o.totalRecords || r.length;
21565         
21566         this.fireEvent("beforeloadadd", this, r, options, o);
21567         
21568         if(!options || options.add !== true){
21569             if(this.pruneModifiedRecords){
21570                 this.modified = [];
21571             }
21572             for(var i = 0, len = r.length; i < len; i++){
21573                 r[i].join(this);
21574             }
21575             if(this.snapshot){
21576                 this.data = this.snapshot;
21577                 delete this.snapshot;
21578             }
21579             this.data.clear();
21580             this.data.addAll(r);
21581             this.totalLength = t;
21582             this.applySort();
21583             this.fireEvent("datachanged", this);
21584         }else{
21585             this.totalLength = Math.max(t, this.data.length+r.length);
21586             this.add(r);
21587         }
21588         this.fireEvent("load", this, r, options, o);
21589         if(options.callback){
21590             options.callback.call(options.scope || this, r, options, true);
21591         }
21592     },
21593
21594
21595     /**
21596      * Loads data from a passed data block. A Reader which understands the format of the data
21597      * must have been configured in the constructor.
21598      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21599      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21600      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21601      */
21602     loadData : function(o, append){
21603         var r = this.reader.readRecords(o);
21604         this.loadRecords(r, {add: append}, true);
21605     },
21606
21607     /**
21608      * Gets the number of cached records.
21609      * <p>
21610      * <em>If using paging, this may not be the total size of the dataset. If the data object
21611      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21612      * the data set size</em>
21613      */
21614     getCount : function(){
21615         return this.data.length || 0;
21616     },
21617
21618     /**
21619      * Gets the total number of records in the dataset as returned by the server.
21620      * <p>
21621      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21622      * the dataset size</em>
21623      */
21624     getTotalCount : function(){
21625         return this.totalLength || 0;
21626     },
21627
21628     /**
21629      * Returns the sort state of the Store as an object with two properties:
21630      * <pre><code>
21631  field {String} The name of the field by which the Records are sorted
21632  direction {String} The sort order, "ASC" or "DESC"
21633      * </code></pre>
21634      */
21635     getSortState : function(){
21636         return this.sortInfo;
21637     },
21638
21639     // private
21640     applySort : function(){
21641         if(this.sortInfo && !this.remoteSort){
21642             var s = this.sortInfo, f = s.field;
21643             var st = this.fields.get(f).sortType;
21644             var fn = function(r1, r2){
21645                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21646                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21647             };
21648             this.data.sort(s.direction, fn);
21649             if(this.snapshot && this.snapshot != this.data){
21650                 this.snapshot.sort(s.direction, fn);
21651             }
21652         }
21653     },
21654
21655     /**
21656      * Sets the default sort column and order to be used by the next load operation.
21657      * @param {String} fieldName The name of the field to sort by.
21658      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21659      */
21660     setDefaultSort : function(field, dir){
21661         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21662     },
21663
21664     /**
21665      * Sort the Records.
21666      * If remote sorting is used, the sort is performed on the server, and the cache is
21667      * reloaded. If local sorting is used, the cache is sorted internally.
21668      * @param {String} fieldName The name of the field to sort by.
21669      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21670      */
21671     sort : function(fieldName, dir){
21672         var f = this.fields.get(fieldName);
21673         if(!dir){
21674             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21675             
21676             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21677                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21678             }else{
21679                 dir = f.sortDir;
21680             }
21681         }
21682         this.sortToggle[f.name] = dir;
21683         this.sortInfo = {field: f.name, direction: dir};
21684         if(!this.remoteSort){
21685             this.applySort();
21686             this.fireEvent("datachanged", this);
21687         }else{
21688             this.load(this.lastOptions);
21689         }
21690     },
21691
21692     /**
21693      * Calls the specified function for each of the Records in the cache.
21694      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21695      * Returning <em>false</em> aborts and exits the iteration.
21696      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21697      */
21698     each : function(fn, scope){
21699         this.data.each(fn, scope);
21700     },
21701
21702     /**
21703      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21704      * (e.g., during paging).
21705      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21706      */
21707     getModifiedRecords : function(){
21708         return this.modified;
21709     },
21710
21711     // private
21712     createFilterFn : function(property, value, anyMatch){
21713         if(!value.exec){ // not a regex
21714             value = String(value);
21715             if(value.length == 0){
21716                 return false;
21717             }
21718             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21719         }
21720         return function(r){
21721             return value.test(r.data[property]);
21722         };
21723     },
21724
21725     /**
21726      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21727      * @param {String} property A field on your records
21728      * @param {Number} start The record index to start at (defaults to 0)
21729      * @param {Number} end The last record index to include (defaults to length - 1)
21730      * @return {Number} The sum
21731      */
21732     sum : function(property, start, end){
21733         var rs = this.data.items, v = 0;
21734         start = start || 0;
21735         end = (end || end === 0) ? end : rs.length-1;
21736
21737         for(var i = start; i <= end; i++){
21738             v += (rs[i].data[property] || 0);
21739         }
21740         return v;
21741     },
21742
21743     /**
21744      * Filter the records by a specified property.
21745      * @param {String} field A field on your records
21746      * @param {String/RegExp} value Either a string that the field
21747      * should start with or a RegExp to test against the field
21748      * @param {Boolean} anyMatch True to match any part not just the beginning
21749      */
21750     filter : function(property, value, anyMatch){
21751         var fn = this.createFilterFn(property, value, anyMatch);
21752         return fn ? this.filterBy(fn) : this.clearFilter();
21753     },
21754
21755     /**
21756      * Filter by a function. The specified function will be called with each
21757      * record in this data source. If the function returns true the record is included,
21758      * otherwise it is filtered.
21759      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21760      * @param {Object} scope (optional) The scope of the function (defaults to this)
21761      */
21762     filterBy : function(fn, scope){
21763         this.snapshot = this.snapshot || this.data;
21764         this.data = this.queryBy(fn, scope||this);
21765         this.fireEvent("datachanged", this);
21766     },
21767
21768     /**
21769      * Query the records by a specified property.
21770      * @param {String} field A field on your records
21771      * @param {String/RegExp} value Either a string that the field
21772      * should start with or a RegExp to test against the field
21773      * @param {Boolean} anyMatch True to match any part not just the beginning
21774      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21775      */
21776     query : function(property, value, anyMatch){
21777         var fn = this.createFilterFn(property, value, anyMatch);
21778         return fn ? this.queryBy(fn) : this.data.clone();
21779     },
21780
21781     /**
21782      * Query by a function. The specified function will be called with each
21783      * record in this data source. If the function returns true the record is included
21784      * in the results.
21785      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21786      * @param {Object} scope (optional) The scope of the function (defaults to this)
21787       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21788      **/
21789     queryBy : function(fn, scope){
21790         var data = this.snapshot || this.data;
21791         return data.filterBy(fn, scope||this);
21792     },
21793
21794     /**
21795      * Collects unique values for a particular dataIndex from this store.
21796      * @param {String} dataIndex The property to collect
21797      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21798      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21799      * @return {Array} An array of the unique values
21800      **/
21801     collect : function(dataIndex, allowNull, bypassFilter){
21802         var d = (bypassFilter === true && this.snapshot) ?
21803                 this.snapshot.items : this.data.items;
21804         var v, sv, r = [], l = {};
21805         for(var i = 0, len = d.length; i < len; i++){
21806             v = d[i].data[dataIndex];
21807             sv = String(v);
21808             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21809                 l[sv] = true;
21810                 r[r.length] = v;
21811             }
21812         }
21813         return r;
21814     },
21815
21816     /**
21817      * Revert to a view of the Record cache with no filtering applied.
21818      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21819      */
21820     clearFilter : function(suppressEvent){
21821         if(this.snapshot && this.snapshot != this.data){
21822             this.data = this.snapshot;
21823             delete this.snapshot;
21824             if(suppressEvent !== true){
21825                 this.fireEvent("datachanged", this);
21826             }
21827         }
21828     },
21829
21830     // private
21831     afterEdit : function(record){
21832         if(this.modified.indexOf(record) == -1){
21833             this.modified.push(record);
21834         }
21835         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21836     },
21837     
21838     // private
21839     afterReject : function(record){
21840         this.modified.remove(record);
21841         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21842     },
21843
21844     // private
21845     afterCommit : function(record){
21846         this.modified.remove(record);
21847         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21848     },
21849
21850     /**
21851      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21852      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21853      */
21854     commitChanges : function(){
21855         var m = this.modified.slice(0);
21856         this.modified = [];
21857         for(var i = 0, len = m.length; i < len; i++){
21858             m[i].commit();
21859         }
21860     },
21861
21862     /**
21863      * Cancel outstanding changes on all changed records.
21864      */
21865     rejectChanges : function(){
21866         var m = this.modified.slice(0);
21867         this.modified = [];
21868         for(var i = 0, len = m.length; i < len; i++){
21869             m[i].reject();
21870         }
21871     },
21872
21873     onMetaChange : function(meta, rtype, o){
21874         this.recordType = rtype;
21875         this.fields = rtype.prototype.fields;
21876         delete this.snapshot;
21877         this.sortInfo = meta.sortInfo || this.sortInfo;
21878         this.modified = [];
21879         this.fireEvent('metachange', this, this.reader.meta);
21880     },
21881     
21882     moveIndex : function(data, type)
21883     {
21884         var index = this.indexOf(data);
21885         
21886         var newIndex = index + type;
21887         
21888         this.remove(data);
21889         
21890         this.insert(newIndex, data);
21891         
21892     }
21893 });/*
21894  * Based on:
21895  * Ext JS Library 1.1.1
21896  * Copyright(c) 2006-2007, Ext JS, LLC.
21897  *
21898  * Originally Released Under LGPL - original licence link has changed is not relivant.
21899  *
21900  * Fork - LGPL
21901  * <script type="text/javascript">
21902  */
21903
21904 /**
21905  * @class Roo.data.SimpleStore
21906  * @extends Roo.data.Store
21907  * Small helper class to make creating Stores from Array data easier.
21908  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21909  * @cfg {Array} fields An array of field definition objects, or field name strings.
21910  * @cfg {Array} data The multi-dimensional array of data
21911  * @constructor
21912  * @param {Object} config
21913  */
21914 Roo.data.SimpleStore = function(config){
21915     Roo.data.SimpleStore.superclass.constructor.call(this, {
21916         isLocal : true,
21917         reader: new Roo.data.ArrayReader({
21918                 id: config.id
21919             },
21920             Roo.data.Record.create(config.fields)
21921         ),
21922         proxy : new Roo.data.MemoryProxy(config.data)
21923     });
21924     this.load();
21925 };
21926 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21927  * Based on:
21928  * Ext JS Library 1.1.1
21929  * Copyright(c) 2006-2007, Ext JS, LLC.
21930  *
21931  * Originally Released Under LGPL - original licence link has changed is not relivant.
21932  *
21933  * Fork - LGPL
21934  * <script type="text/javascript">
21935  */
21936
21937 /**
21938 /**
21939  * @extends Roo.data.Store
21940  * @class Roo.data.JsonStore
21941  * Small helper class to make creating Stores for JSON data easier. <br/>
21942 <pre><code>
21943 var store = new Roo.data.JsonStore({
21944     url: 'get-images.php',
21945     root: 'images',
21946     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21947 });
21948 </code></pre>
21949  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21950  * JsonReader and HttpProxy (unless inline data is provided).</b>
21951  * @cfg {Array} fields An array of field definition objects, or field name strings.
21952  * @constructor
21953  * @param {Object} config
21954  */
21955 Roo.data.JsonStore = function(c){
21956     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21957         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21958         reader: new Roo.data.JsonReader(c, c.fields)
21959     }));
21960 };
21961 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21962  * Based on:
21963  * Ext JS Library 1.1.1
21964  * Copyright(c) 2006-2007, Ext JS, LLC.
21965  *
21966  * Originally Released Under LGPL - original licence link has changed is not relivant.
21967  *
21968  * Fork - LGPL
21969  * <script type="text/javascript">
21970  */
21971
21972  
21973 Roo.data.Field = function(config){
21974     if(typeof config == "string"){
21975         config = {name: config};
21976     }
21977     Roo.apply(this, config);
21978     
21979     if(!this.type){
21980         this.type = "auto";
21981     }
21982     
21983     var st = Roo.data.SortTypes;
21984     // named sortTypes are supported, here we look them up
21985     if(typeof this.sortType == "string"){
21986         this.sortType = st[this.sortType];
21987     }
21988     
21989     // set default sortType for strings and dates
21990     if(!this.sortType){
21991         switch(this.type){
21992             case "string":
21993                 this.sortType = st.asUCString;
21994                 break;
21995             case "date":
21996                 this.sortType = st.asDate;
21997                 break;
21998             default:
21999                 this.sortType = st.none;
22000         }
22001     }
22002
22003     // define once
22004     var stripRe = /[\$,%]/g;
22005
22006     // prebuilt conversion function for this field, instead of
22007     // switching every time we're reading a value
22008     if(!this.convert){
22009         var cv, dateFormat = this.dateFormat;
22010         switch(this.type){
22011             case "":
22012             case "auto":
22013             case undefined:
22014                 cv = function(v){ return v; };
22015                 break;
22016             case "string":
22017                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22018                 break;
22019             case "int":
22020                 cv = function(v){
22021                     return v !== undefined && v !== null && v !== '' ?
22022                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22023                     };
22024                 break;
22025             case "float":
22026                 cv = function(v){
22027                     return v !== undefined && v !== null && v !== '' ?
22028                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22029                     };
22030                 break;
22031             case "bool":
22032             case "boolean":
22033                 cv = function(v){ return v === true || v === "true" || v == 1; };
22034                 break;
22035             case "date":
22036                 cv = function(v){
22037                     if(!v){
22038                         return '';
22039                     }
22040                     if(v instanceof Date){
22041                         return v;
22042                     }
22043                     if(dateFormat){
22044                         if(dateFormat == "timestamp"){
22045                             return new Date(v*1000);
22046                         }
22047                         return Date.parseDate(v, dateFormat);
22048                     }
22049                     var parsed = Date.parse(v);
22050                     return parsed ? new Date(parsed) : null;
22051                 };
22052              break;
22053             
22054         }
22055         this.convert = cv;
22056     }
22057 };
22058
22059 Roo.data.Field.prototype = {
22060     dateFormat: null,
22061     defaultValue: "",
22062     mapping: null,
22063     sortType : null,
22064     sortDir : "ASC"
22065 };/*
22066  * Based on:
22067  * Ext JS Library 1.1.1
22068  * Copyright(c) 2006-2007, Ext JS, LLC.
22069  *
22070  * Originally Released Under LGPL - original licence link has changed is not relivant.
22071  *
22072  * Fork - LGPL
22073  * <script type="text/javascript">
22074  */
22075  
22076 // Base class for reading structured data from a data source.  This class is intended to be
22077 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22078
22079 /**
22080  * @class Roo.data.DataReader
22081  * Base class for reading structured data from a data source.  This class is intended to be
22082  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22083  */
22084
22085 Roo.data.DataReader = function(meta, recordType){
22086     
22087     this.meta = meta;
22088     
22089     this.recordType = recordType instanceof Array ? 
22090         Roo.data.Record.create(recordType) : recordType;
22091 };
22092
22093 Roo.data.DataReader.prototype = {
22094      /**
22095      * Create an empty record
22096      * @param {Object} data (optional) - overlay some values
22097      * @return {Roo.data.Record} record created.
22098      */
22099     newRow :  function(d) {
22100         var da =  {};
22101         this.recordType.prototype.fields.each(function(c) {
22102             switch( c.type) {
22103                 case 'int' : da[c.name] = 0; break;
22104                 case 'date' : da[c.name] = new Date(); break;
22105                 case 'float' : da[c.name] = 0.0; break;
22106                 case 'boolean' : da[c.name] = false; break;
22107                 default : da[c.name] = ""; break;
22108             }
22109             
22110         });
22111         return new this.recordType(Roo.apply(da, d));
22112     }
22113     
22114 };/*
22115  * Based on:
22116  * Ext JS Library 1.1.1
22117  * Copyright(c) 2006-2007, Ext JS, LLC.
22118  *
22119  * Originally Released Under LGPL - original licence link has changed is not relivant.
22120  *
22121  * Fork - LGPL
22122  * <script type="text/javascript">
22123  */
22124
22125 /**
22126  * @class Roo.data.DataProxy
22127  * @extends Roo.data.Observable
22128  * This class is an abstract base class for implementations which provide retrieval of
22129  * unformatted data objects.<br>
22130  * <p>
22131  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22132  * (of the appropriate type which knows how to parse the data object) to provide a block of
22133  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22134  * <p>
22135  * Custom implementations must implement the load method as described in
22136  * {@link Roo.data.HttpProxy#load}.
22137  */
22138 Roo.data.DataProxy = function(){
22139     this.addEvents({
22140         /**
22141          * @event beforeload
22142          * Fires before a network request is made to retrieve a data object.
22143          * @param {Object} This DataProxy object.
22144          * @param {Object} params The params parameter to the load function.
22145          */
22146         beforeload : true,
22147         /**
22148          * @event load
22149          * Fires before the load method's callback is called.
22150          * @param {Object} This DataProxy object.
22151          * @param {Object} o The data object.
22152          * @param {Object} arg The callback argument object passed to the load function.
22153          */
22154         load : true,
22155         /**
22156          * @event loadexception
22157          * Fires if an Exception occurs during data retrieval.
22158          * @param {Object} This DataProxy object.
22159          * @param {Object} o The data object.
22160          * @param {Object} arg The callback argument object passed to the load function.
22161          * @param {Object} e The Exception.
22162          */
22163         loadexception : true
22164     });
22165     Roo.data.DataProxy.superclass.constructor.call(this);
22166 };
22167
22168 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22169
22170     /**
22171      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22172      */
22173 /*
22174  * Based on:
22175  * Ext JS Library 1.1.1
22176  * Copyright(c) 2006-2007, Ext JS, LLC.
22177  *
22178  * Originally Released Under LGPL - original licence link has changed is not relivant.
22179  *
22180  * Fork - LGPL
22181  * <script type="text/javascript">
22182  */
22183 /**
22184  * @class Roo.data.MemoryProxy
22185  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22186  * to the Reader when its load method is called.
22187  * @constructor
22188  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22189  */
22190 Roo.data.MemoryProxy = function(data){
22191     if (data.data) {
22192         data = data.data;
22193     }
22194     Roo.data.MemoryProxy.superclass.constructor.call(this);
22195     this.data = data;
22196 };
22197
22198 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22199     /**
22200      * Load data from the requested source (in this case an in-memory
22201      * data object passed to the constructor), read the data object into
22202      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22203      * process that block using the passed callback.
22204      * @param {Object} params This parameter is not used by the MemoryProxy class.
22205      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22206      * object into a block of Roo.data.Records.
22207      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22208      * The function must be passed <ul>
22209      * <li>The Record block object</li>
22210      * <li>The "arg" argument from the load function</li>
22211      * <li>A boolean success indicator</li>
22212      * </ul>
22213      * @param {Object} scope The scope in which to call the callback
22214      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22215      */
22216     load : function(params, reader, callback, scope, arg){
22217         params = params || {};
22218         var result;
22219         try {
22220             result = reader.readRecords(this.data);
22221         }catch(e){
22222             this.fireEvent("loadexception", this, arg, null, e);
22223             callback.call(scope, null, arg, false);
22224             return;
22225         }
22226         callback.call(scope, result, arg, true);
22227     },
22228     
22229     // private
22230     update : function(params, records){
22231         
22232     }
22233 });/*
22234  * Based on:
22235  * Ext JS Library 1.1.1
22236  * Copyright(c) 2006-2007, Ext JS, LLC.
22237  *
22238  * Originally Released Under LGPL - original licence link has changed is not relivant.
22239  *
22240  * Fork - LGPL
22241  * <script type="text/javascript">
22242  */
22243 /**
22244  * @class Roo.data.HttpProxy
22245  * @extends Roo.data.DataProxy
22246  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22247  * configured to reference a certain URL.<br><br>
22248  * <p>
22249  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22250  * from which the running page was served.<br><br>
22251  * <p>
22252  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22253  * <p>
22254  * Be aware that to enable the browser to parse an XML document, the server must set
22255  * the Content-Type header in the HTTP response to "text/xml".
22256  * @constructor
22257  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22258  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22259  * will be used to make the request.
22260  */
22261 Roo.data.HttpProxy = function(conn){
22262     Roo.data.HttpProxy.superclass.constructor.call(this);
22263     // is conn a conn config or a real conn?
22264     this.conn = conn;
22265     this.useAjax = !conn || !conn.events;
22266   
22267 };
22268
22269 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22270     // thse are take from connection...
22271     
22272     /**
22273      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22274      */
22275     /**
22276      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22277      * extra parameters to each request made by this object. (defaults to undefined)
22278      */
22279     /**
22280      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22281      *  to each request made by this object. (defaults to undefined)
22282      */
22283     /**
22284      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
22285      */
22286     /**
22287      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22288      */
22289      /**
22290      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22291      * @type Boolean
22292      */
22293   
22294
22295     /**
22296      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22297      * @type Boolean
22298      */
22299     /**
22300      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22301      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22302      * a finer-grained basis than the DataProxy events.
22303      */
22304     getConnection : function(){
22305         return this.useAjax ? Roo.Ajax : this.conn;
22306     },
22307
22308     /**
22309      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22310      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22311      * process that block using the passed callback.
22312      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22313      * for the request to the remote server.
22314      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22315      * object into a block of Roo.data.Records.
22316      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22317      * The function must be passed <ul>
22318      * <li>The Record block object</li>
22319      * <li>The "arg" argument from the load function</li>
22320      * <li>A boolean success indicator</li>
22321      * </ul>
22322      * @param {Object} scope The scope in which to call the callback
22323      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22324      */
22325     load : function(params, reader, callback, scope, arg){
22326         if(this.fireEvent("beforeload", this, params) !== false){
22327             var  o = {
22328                 params : params || {},
22329                 request: {
22330                     callback : callback,
22331                     scope : scope,
22332                     arg : arg
22333                 },
22334                 reader: reader,
22335                 callback : this.loadResponse,
22336                 scope: this
22337             };
22338             if(this.useAjax){
22339                 Roo.applyIf(o, this.conn);
22340                 if(this.activeRequest){
22341                     Roo.Ajax.abort(this.activeRequest);
22342                 }
22343                 this.activeRequest = Roo.Ajax.request(o);
22344             }else{
22345                 this.conn.request(o);
22346             }
22347         }else{
22348             callback.call(scope||this, null, arg, false);
22349         }
22350     },
22351
22352     // private
22353     loadResponse : function(o, success, response){
22354         delete this.activeRequest;
22355         if(!success){
22356             this.fireEvent("loadexception", this, o, response);
22357             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22358             return;
22359         }
22360         var result;
22361         try {
22362             result = o.reader.read(response);
22363         }catch(e){
22364             this.fireEvent("loadexception", this, o, response, e);
22365             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22366             return;
22367         }
22368         
22369         this.fireEvent("load", this, o, o.request.arg);
22370         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22371     },
22372
22373     // private
22374     update : function(dataSet){
22375
22376     },
22377
22378     // private
22379     updateResponse : function(dataSet){
22380
22381     }
22382 });/*
22383  * Based on:
22384  * Ext JS Library 1.1.1
22385  * Copyright(c) 2006-2007, Ext JS, LLC.
22386  *
22387  * Originally Released Under LGPL - original licence link has changed is not relivant.
22388  *
22389  * Fork - LGPL
22390  * <script type="text/javascript">
22391  */
22392
22393 /**
22394  * @class Roo.data.ScriptTagProxy
22395  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22396  * other than the originating domain of the running page.<br><br>
22397  * <p>
22398  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
22399  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22400  * <p>
22401  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22402  * source code that is used as the source inside a &lt;script> tag.<br><br>
22403  * <p>
22404  * In order for the browser to process the returned data, the server must wrap the data object
22405  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22406  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22407  * depending on whether the callback name was passed:
22408  * <p>
22409  * <pre><code>
22410 boolean scriptTag = false;
22411 String cb = request.getParameter("callback");
22412 if (cb != null) {
22413     scriptTag = true;
22414     response.setContentType("text/javascript");
22415 } else {
22416     response.setContentType("application/x-json");
22417 }
22418 Writer out = response.getWriter();
22419 if (scriptTag) {
22420     out.write(cb + "(");
22421 }
22422 out.print(dataBlock.toJsonString());
22423 if (scriptTag) {
22424     out.write(");");
22425 }
22426 </pre></code>
22427  *
22428  * @constructor
22429  * @param {Object} config A configuration object.
22430  */
22431 Roo.data.ScriptTagProxy = function(config){
22432     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22433     Roo.apply(this, config);
22434     this.head = document.getElementsByTagName("head")[0];
22435 };
22436
22437 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22438
22439 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22440     /**
22441      * @cfg {String} url The URL from which to request the data object.
22442      */
22443     /**
22444      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22445      */
22446     timeout : 30000,
22447     /**
22448      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22449      * the server the name of the callback function set up by the load call to process the returned data object.
22450      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22451      * javascript output which calls this named function passing the data object as its only parameter.
22452      */
22453     callbackParam : "callback",
22454     /**
22455      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22456      * name to the request.
22457      */
22458     nocache : true,
22459
22460     /**
22461      * Load data from the configured URL, read the data object into
22462      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22463      * process that block using the passed callback.
22464      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22465      * for the request to the remote server.
22466      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22467      * object into a block of Roo.data.Records.
22468      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22469      * The function must be passed <ul>
22470      * <li>The Record block object</li>
22471      * <li>The "arg" argument from the load function</li>
22472      * <li>A boolean success indicator</li>
22473      * </ul>
22474      * @param {Object} scope The scope in which to call the callback
22475      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22476      */
22477     load : function(params, reader, callback, scope, arg){
22478         if(this.fireEvent("beforeload", this, params) !== false){
22479
22480             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22481
22482             var url = this.url;
22483             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22484             if(this.nocache){
22485                 url += "&_dc=" + (new Date().getTime());
22486             }
22487             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22488             var trans = {
22489                 id : transId,
22490                 cb : "stcCallback"+transId,
22491                 scriptId : "stcScript"+transId,
22492                 params : params,
22493                 arg : arg,
22494                 url : url,
22495                 callback : callback,
22496                 scope : scope,
22497                 reader : reader
22498             };
22499             var conn = this;
22500
22501             window[trans.cb] = function(o){
22502                 conn.handleResponse(o, trans);
22503             };
22504
22505             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22506
22507             if(this.autoAbort !== false){
22508                 this.abort();
22509             }
22510
22511             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22512
22513             var script = document.createElement("script");
22514             script.setAttribute("src", url);
22515             script.setAttribute("type", "text/javascript");
22516             script.setAttribute("id", trans.scriptId);
22517             this.head.appendChild(script);
22518
22519             this.trans = trans;
22520         }else{
22521             callback.call(scope||this, null, arg, false);
22522         }
22523     },
22524
22525     // private
22526     isLoading : function(){
22527         return this.trans ? true : false;
22528     },
22529
22530     /**
22531      * Abort the current server request.
22532      */
22533     abort : function(){
22534         if(this.isLoading()){
22535             this.destroyTrans(this.trans);
22536         }
22537     },
22538
22539     // private
22540     destroyTrans : function(trans, isLoaded){
22541         this.head.removeChild(document.getElementById(trans.scriptId));
22542         clearTimeout(trans.timeoutId);
22543         if(isLoaded){
22544             window[trans.cb] = undefined;
22545             try{
22546                 delete window[trans.cb];
22547             }catch(e){}
22548         }else{
22549             // if hasn't been loaded, wait for load to remove it to prevent script error
22550             window[trans.cb] = function(){
22551                 window[trans.cb] = undefined;
22552                 try{
22553                     delete window[trans.cb];
22554                 }catch(e){}
22555             };
22556         }
22557     },
22558
22559     // private
22560     handleResponse : function(o, trans){
22561         this.trans = false;
22562         this.destroyTrans(trans, true);
22563         var result;
22564         try {
22565             result = trans.reader.readRecords(o);
22566         }catch(e){
22567             this.fireEvent("loadexception", this, o, trans.arg, e);
22568             trans.callback.call(trans.scope||window, null, trans.arg, false);
22569             return;
22570         }
22571         this.fireEvent("load", this, o, trans.arg);
22572         trans.callback.call(trans.scope||window, result, trans.arg, true);
22573     },
22574
22575     // private
22576     handleFailure : function(trans){
22577         this.trans = false;
22578         this.destroyTrans(trans, false);
22579         this.fireEvent("loadexception", this, null, trans.arg);
22580         trans.callback.call(trans.scope||window, null, trans.arg, false);
22581     }
22582 });/*
22583  * Based on:
22584  * Ext JS Library 1.1.1
22585  * Copyright(c) 2006-2007, Ext JS, LLC.
22586  *
22587  * Originally Released Under LGPL - original licence link has changed is not relivant.
22588  *
22589  * Fork - LGPL
22590  * <script type="text/javascript">
22591  */
22592
22593 /**
22594  * @class Roo.data.JsonReader
22595  * @extends Roo.data.DataReader
22596  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22597  * based on mappings in a provided Roo.data.Record constructor.
22598  * 
22599  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22600  * in the reply previously. 
22601  * 
22602  * <p>
22603  * Example code:
22604  * <pre><code>
22605 var RecordDef = Roo.data.Record.create([
22606     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22607     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22608 ]);
22609 var myReader = new Roo.data.JsonReader({
22610     totalProperty: "results",    // The property which contains the total dataset size (optional)
22611     root: "rows",                // The property which contains an Array of row objects
22612     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22613 }, RecordDef);
22614 </code></pre>
22615  * <p>
22616  * This would consume a JSON file like this:
22617  * <pre><code>
22618 { 'results': 2, 'rows': [
22619     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22620     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22621 }
22622 </code></pre>
22623  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22624  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22625  * paged from the remote server.
22626  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22627  * @cfg {String} root name of the property which contains the Array of row objects.
22628  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22629  * @cfg {Array} fields Array of field definition objects
22630  * @constructor
22631  * Create a new JsonReader
22632  * @param {Object} meta Metadata configuration options
22633  * @param {Object} recordType Either an Array of field definition objects,
22634  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22635  */
22636 Roo.data.JsonReader = function(meta, recordType){
22637     
22638     meta = meta || {};
22639     // set some defaults:
22640     Roo.applyIf(meta, {
22641         totalProperty: 'total',
22642         successProperty : 'success',
22643         root : 'data',
22644         id : 'id'
22645     });
22646     
22647     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22648 };
22649 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22650     
22651     /**
22652      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22653      * Used by Store query builder to append _requestMeta to params.
22654      * 
22655      */
22656     metaFromRemote : false,
22657     /**
22658      * This method is only used by a DataProxy which has retrieved data from a remote server.
22659      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22660      * @return {Object} data A data block which is used by an Roo.data.Store object as
22661      * a cache of Roo.data.Records.
22662      */
22663     read : function(response){
22664         var json = response.responseText;
22665        
22666         var o = /* eval:var:o */ eval("("+json+")");
22667         if(!o) {
22668             throw {message: "JsonReader.read: Json object not found"};
22669         }
22670         
22671         if(o.metaData){
22672             
22673             delete this.ef;
22674             this.metaFromRemote = true;
22675             this.meta = o.metaData;
22676             this.recordType = Roo.data.Record.create(o.metaData.fields);
22677             this.onMetaChange(this.meta, this.recordType, o);
22678         }
22679         return this.readRecords(o);
22680     },
22681
22682     // private function a store will implement
22683     onMetaChange : function(meta, recordType, o){
22684
22685     },
22686
22687     /**
22688          * @ignore
22689          */
22690     simpleAccess: function(obj, subsc) {
22691         return obj[subsc];
22692     },
22693
22694         /**
22695          * @ignore
22696          */
22697     getJsonAccessor: function(){
22698         var re = /[\[\.]/;
22699         return function(expr) {
22700             try {
22701                 return(re.test(expr))
22702                     ? new Function("obj", "return obj." + expr)
22703                     : function(obj){
22704                         return obj[expr];
22705                     };
22706             } catch(e){}
22707             return Roo.emptyFn;
22708         };
22709     }(),
22710
22711     /**
22712      * Create a data block containing Roo.data.Records from an XML document.
22713      * @param {Object} o An object which contains an Array of row objects in the property specified
22714      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22715      * which contains the total size of the dataset.
22716      * @return {Object} data A data block which is used by an Roo.data.Store object as
22717      * a cache of Roo.data.Records.
22718      */
22719     readRecords : function(o){
22720         /**
22721          * After any data loads, the raw JSON data is available for further custom processing.
22722          * @type Object
22723          */
22724         this.o = o;
22725         var s = this.meta, Record = this.recordType,
22726             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22727
22728 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22729         if (!this.ef) {
22730             if(s.totalProperty) {
22731                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22732                 }
22733                 if(s.successProperty) {
22734                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22735                 }
22736                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22737                 if (s.id) {
22738                         var g = this.getJsonAccessor(s.id);
22739                         this.getId = function(rec) {
22740                                 var r = g(rec);  
22741                                 return (r === undefined || r === "") ? null : r;
22742                         };
22743                 } else {
22744                         this.getId = function(){return null;};
22745                 }
22746             this.ef = [];
22747             for(var jj = 0; jj < fl; jj++){
22748                 f = fi[jj];
22749                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22750                 this.ef[jj] = this.getJsonAccessor(map);
22751             }
22752         }
22753
22754         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22755         if(s.totalProperty){
22756             var vt = parseInt(this.getTotal(o), 10);
22757             if(!isNaN(vt)){
22758                 totalRecords = vt;
22759             }
22760         }
22761         if(s.successProperty){
22762             var vs = this.getSuccess(o);
22763             if(vs === false || vs === 'false'){
22764                 success = false;
22765             }
22766         }
22767         var records = [];
22768         for(var i = 0; i < c; i++){
22769                 var n = root[i];
22770             var values = {};
22771             var id = this.getId(n);
22772             for(var j = 0; j < fl; j++){
22773                 f = fi[j];
22774             var v = this.ef[j](n);
22775             if (!f.convert) {
22776                 Roo.log('missing convert for ' + f.name);
22777                 Roo.log(f);
22778                 continue;
22779             }
22780             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22781             }
22782             var record = new Record(values, id);
22783             record.json = n;
22784             records[i] = record;
22785         }
22786         return {
22787             raw : o,
22788             success : success,
22789             records : records,
22790             totalRecords : totalRecords
22791         };
22792     }
22793 });/*
22794  * Based on:
22795  * Ext JS Library 1.1.1
22796  * Copyright(c) 2006-2007, Ext JS, LLC.
22797  *
22798  * Originally Released Under LGPL - original licence link has changed is not relivant.
22799  *
22800  * Fork - LGPL
22801  * <script type="text/javascript">
22802  */
22803
22804 /**
22805  * @class Roo.data.XmlReader
22806  * @extends Roo.data.DataReader
22807  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22808  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22809  * <p>
22810  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22811  * header in the HTTP response must be set to "text/xml".</em>
22812  * <p>
22813  * Example code:
22814  * <pre><code>
22815 var RecordDef = Roo.data.Record.create([
22816    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22817    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22818 ]);
22819 var myReader = new Roo.data.XmlReader({
22820    totalRecords: "results", // The element which contains the total dataset size (optional)
22821    record: "row",           // The repeated element which contains row information
22822    id: "id"                 // The element within the row that provides an ID for the record (optional)
22823 }, RecordDef);
22824 </code></pre>
22825  * <p>
22826  * This would consume an XML file like this:
22827  * <pre><code>
22828 &lt;?xml?>
22829 &lt;dataset>
22830  &lt;results>2&lt;/results>
22831  &lt;row>
22832    &lt;id>1&lt;/id>
22833    &lt;name>Bill&lt;/name>
22834    &lt;occupation>Gardener&lt;/occupation>
22835  &lt;/row>
22836  &lt;row>
22837    &lt;id>2&lt;/id>
22838    &lt;name>Ben&lt;/name>
22839    &lt;occupation>Horticulturalist&lt;/occupation>
22840  &lt;/row>
22841 &lt;/dataset>
22842 </code></pre>
22843  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22844  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22845  * paged from the remote server.
22846  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22847  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22848  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22849  * a record identifier value.
22850  * @constructor
22851  * Create a new XmlReader
22852  * @param {Object} meta Metadata configuration options
22853  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22854  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22855  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22856  */
22857 Roo.data.XmlReader = function(meta, recordType){
22858     meta = meta || {};
22859     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22860 };
22861 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22862     /**
22863      * This method is only used by a DataProxy which has retrieved data from a remote server.
22864          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22865          * to contain a method called 'responseXML' that returns an XML document object.
22866      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22867      * a cache of Roo.data.Records.
22868      */
22869     read : function(response){
22870         var doc = response.responseXML;
22871         if(!doc) {
22872             throw {message: "XmlReader.read: XML Document not available"};
22873         }
22874         return this.readRecords(doc);
22875     },
22876
22877     /**
22878      * Create a data block containing Roo.data.Records from an XML document.
22879          * @param {Object} doc A parsed XML document.
22880      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22881      * a cache of Roo.data.Records.
22882      */
22883     readRecords : function(doc){
22884         /**
22885          * After any data loads/reads, the raw XML Document is available for further custom processing.
22886          * @type XMLDocument
22887          */
22888         this.xmlData = doc;
22889         var root = doc.documentElement || doc;
22890         var q = Roo.DomQuery;
22891         var recordType = this.recordType, fields = recordType.prototype.fields;
22892         var sid = this.meta.id;
22893         var totalRecords = 0, success = true;
22894         if(this.meta.totalRecords){
22895             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22896         }
22897         
22898         if(this.meta.success){
22899             var sv = q.selectValue(this.meta.success, root, true);
22900             success = sv !== false && sv !== 'false';
22901         }
22902         var records = [];
22903         var ns = q.select(this.meta.record, root);
22904         for(var i = 0, len = ns.length; i < len; i++) {
22905                 var n = ns[i];
22906                 var values = {};
22907                 var id = sid ? q.selectValue(sid, n) : undefined;
22908                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22909                     var f = fields.items[j];
22910                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22911                     v = f.convert(v);
22912                     values[f.name] = v;
22913                 }
22914                 var record = new recordType(values, id);
22915                 record.node = n;
22916                 records[records.length] = record;
22917             }
22918
22919             return {
22920                 success : success,
22921                 records : records,
22922                 totalRecords : totalRecords || records.length
22923             };
22924     }
22925 });/*
22926  * Based on:
22927  * Ext JS Library 1.1.1
22928  * Copyright(c) 2006-2007, Ext JS, LLC.
22929  *
22930  * Originally Released Under LGPL - original licence link has changed is not relivant.
22931  *
22932  * Fork - LGPL
22933  * <script type="text/javascript">
22934  */
22935
22936 /**
22937  * @class Roo.data.ArrayReader
22938  * @extends Roo.data.DataReader
22939  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22940  * Each element of that Array represents a row of data fields. The
22941  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22942  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22943  * <p>
22944  * Example code:.
22945  * <pre><code>
22946 var RecordDef = Roo.data.Record.create([
22947     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22948     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22949 ]);
22950 var myReader = new Roo.data.ArrayReader({
22951     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22952 }, RecordDef);
22953 </code></pre>
22954  * <p>
22955  * This would consume an Array like this:
22956  * <pre><code>
22957 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22958   </code></pre>
22959  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22960  * @constructor
22961  * Create a new JsonReader
22962  * @param {Object} meta Metadata configuration options.
22963  * @param {Object} recordType Either an Array of field definition objects
22964  * as specified to {@link Roo.data.Record#create},
22965  * or an {@link Roo.data.Record} object
22966  * created using {@link Roo.data.Record#create}.
22967  */
22968 Roo.data.ArrayReader = function(meta, recordType){
22969     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22970 };
22971
22972 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22973     /**
22974      * Create a data block containing Roo.data.Records from an XML document.
22975      * @param {Object} o An Array of row objects which represents the dataset.
22976      * @return {Object} data A data block which is used by an Roo.data.Store object as
22977      * a cache of Roo.data.Records.
22978      */
22979     readRecords : function(o){
22980         var sid = this.meta ? this.meta.id : null;
22981         var recordType = this.recordType, fields = recordType.prototype.fields;
22982         var records = [];
22983         var root = o;
22984             for(var i = 0; i < root.length; i++){
22985                     var n = root[i];
22986                 var values = {};
22987                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22988                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22989                 var f = fields.items[j];
22990                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22991                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22992                 v = f.convert(v);
22993                 values[f.name] = v;
22994             }
22995                 var record = new recordType(values, id);
22996                 record.json = n;
22997                 records[records.length] = record;
22998             }
22999             return {
23000                 records : records,
23001                 totalRecords : records.length
23002             };
23003     }
23004 });/*
23005  * Based on:
23006  * Ext JS Library 1.1.1
23007  * Copyright(c) 2006-2007, Ext JS, LLC.
23008  *
23009  * Originally Released Under LGPL - original licence link has changed is not relivant.
23010  *
23011  * Fork - LGPL
23012  * <script type="text/javascript">
23013  */
23014
23015
23016 /**
23017  * @class Roo.data.Tree
23018  * @extends Roo.util.Observable
23019  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23020  * in the tree have most standard DOM functionality.
23021  * @constructor
23022  * @param {Node} root (optional) The root node
23023  */
23024 Roo.data.Tree = function(root){
23025    this.nodeHash = {};
23026    /**
23027     * The root node for this tree
23028     * @type Node
23029     */
23030    this.root = null;
23031    if(root){
23032        this.setRootNode(root);
23033    }
23034    this.addEvents({
23035        /**
23036         * @event append
23037         * Fires when a new child node is appended to a node in this tree.
23038         * @param {Tree} tree The owner tree
23039         * @param {Node} parent The parent node
23040         * @param {Node} node The newly appended node
23041         * @param {Number} index The index of the newly appended node
23042         */
23043        "append" : true,
23044        /**
23045         * @event remove
23046         * Fires when a child node is removed from a node in this tree.
23047         * @param {Tree} tree The owner tree
23048         * @param {Node} parent The parent node
23049         * @param {Node} node The child node removed
23050         */
23051        "remove" : true,
23052        /**
23053         * @event move
23054         * Fires when a node is moved to a new location in the tree
23055         * @param {Tree} tree The owner tree
23056         * @param {Node} node The node moved
23057         * @param {Node} oldParent The old parent of this node
23058         * @param {Node} newParent The new parent of this node
23059         * @param {Number} index The index it was moved to
23060         */
23061        "move" : true,
23062        /**
23063         * @event insert
23064         * Fires when a new child node is inserted in a node in this tree.
23065         * @param {Tree} tree The owner tree
23066         * @param {Node} parent The parent node
23067         * @param {Node} node The child node inserted
23068         * @param {Node} refNode The child node the node was inserted before
23069         */
23070        "insert" : true,
23071        /**
23072         * @event beforeappend
23073         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23074         * @param {Tree} tree The owner tree
23075         * @param {Node} parent The parent node
23076         * @param {Node} node The child node to be appended
23077         */
23078        "beforeappend" : true,
23079        /**
23080         * @event beforeremove
23081         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23082         * @param {Tree} tree The owner tree
23083         * @param {Node} parent The parent node
23084         * @param {Node} node The child node to be removed
23085         */
23086        "beforeremove" : true,
23087        /**
23088         * @event beforemove
23089         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23090         * @param {Tree} tree The owner tree
23091         * @param {Node} node The node being moved
23092         * @param {Node} oldParent The parent of the node
23093         * @param {Node} newParent The new parent the node is moving to
23094         * @param {Number} index The index it is being moved to
23095         */
23096        "beforemove" : true,
23097        /**
23098         * @event beforeinsert
23099         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23100         * @param {Tree} tree The owner tree
23101         * @param {Node} parent The parent node
23102         * @param {Node} node The child node to be inserted
23103         * @param {Node} refNode The child node the node is being inserted before
23104         */
23105        "beforeinsert" : true
23106    });
23107
23108     Roo.data.Tree.superclass.constructor.call(this);
23109 };
23110
23111 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23112     pathSeparator: "/",
23113
23114     proxyNodeEvent : function(){
23115         return this.fireEvent.apply(this, arguments);
23116     },
23117
23118     /**
23119      * Returns the root node for this tree.
23120      * @return {Node}
23121      */
23122     getRootNode : function(){
23123         return this.root;
23124     },
23125
23126     /**
23127      * Sets the root node for this tree.
23128      * @param {Node} node
23129      * @return {Node}
23130      */
23131     setRootNode : function(node){
23132         this.root = node;
23133         node.ownerTree = this;
23134         node.isRoot = true;
23135         this.registerNode(node);
23136         return node;
23137     },
23138
23139     /**
23140      * Gets a node in this tree by its id.
23141      * @param {String} id
23142      * @return {Node}
23143      */
23144     getNodeById : function(id){
23145         return this.nodeHash[id];
23146     },
23147
23148     registerNode : function(node){
23149         this.nodeHash[node.id] = node;
23150     },
23151
23152     unregisterNode : function(node){
23153         delete this.nodeHash[node.id];
23154     },
23155
23156     toString : function(){
23157         return "[Tree"+(this.id?" "+this.id:"")+"]";
23158     }
23159 });
23160
23161 /**
23162  * @class Roo.data.Node
23163  * @extends Roo.util.Observable
23164  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23165  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23166  * @constructor
23167  * @param {Object} attributes The attributes/config for the node
23168  */
23169 Roo.data.Node = function(attributes){
23170     /**
23171      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23172      * @type {Object}
23173      */
23174     this.attributes = attributes || {};
23175     this.leaf = this.attributes.leaf;
23176     /**
23177      * The node id. @type String
23178      */
23179     this.id = this.attributes.id;
23180     if(!this.id){
23181         this.id = Roo.id(null, "ynode-");
23182         this.attributes.id = this.id;
23183     }
23184      
23185     
23186     /**
23187      * All child nodes of this node. @type Array
23188      */
23189     this.childNodes = [];
23190     if(!this.childNodes.indexOf){ // indexOf is a must
23191         this.childNodes.indexOf = function(o){
23192             for(var i = 0, len = this.length; i < len; i++){
23193                 if(this[i] == o) {
23194                     return i;
23195                 }
23196             }
23197             return -1;
23198         };
23199     }
23200     /**
23201      * The parent node for this node. @type Node
23202      */
23203     this.parentNode = null;
23204     /**
23205      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23206      */
23207     this.firstChild = null;
23208     /**
23209      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23210      */
23211     this.lastChild = null;
23212     /**
23213      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23214      */
23215     this.previousSibling = null;
23216     /**
23217      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23218      */
23219     this.nextSibling = null;
23220
23221     this.addEvents({
23222        /**
23223         * @event append
23224         * Fires when a new child node is appended
23225         * @param {Tree} tree The owner tree
23226         * @param {Node} this This node
23227         * @param {Node} node The newly appended node
23228         * @param {Number} index The index of the newly appended node
23229         */
23230        "append" : true,
23231        /**
23232         * @event remove
23233         * Fires when a child node is removed
23234         * @param {Tree} tree The owner tree
23235         * @param {Node} this This node
23236         * @param {Node} node The removed node
23237         */
23238        "remove" : true,
23239        /**
23240         * @event move
23241         * Fires when this node is moved to a new location in the tree
23242         * @param {Tree} tree The owner tree
23243         * @param {Node} this This node
23244         * @param {Node} oldParent The old parent of this node
23245         * @param {Node} newParent The new parent of this node
23246         * @param {Number} index The index it was moved to
23247         */
23248        "move" : true,
23249        /**
23250         * @event insert
23251         * Fires when a new child node is inserted.
23252         * @param {Tree} tree The owner tree
23253         * @param {Node} this This node
23254         * @param {Node} node The child node inserted
23255         * @param {Node} refNode The child node the node was inserted before
23256         */
23257        "insert" : true,
23258        /**
23259         * @event beforeappend
23260         * Fires before a new child is appended, return false to cancel the append.
23261         * @param {Tree} tree The owner tree
23262         * @param {Node} this This node
23263         * @param {Node} node The child node to be appended
23264         */
23265        "beforeappend" : true,
23266        /**
23267         * @event beforeremove
23268         * Fires before a child is removed, return false to cancel the remove.
23269         * @param {Tree} tree The owner tree
23270         * @param {Node} this This node
23271         * @param {Node} node The child node to be removed
23272         */
23273        "beforeremove" : true,
23274        /**
23275         * @event beforemove
23276         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23277         * @param {Tree} tree The owner tree
23278         * @param {Node} this This node
23279         * @param {Node} oldParent The parent of this node
23280         * @param {Node} newParent The new parent this node is moving to
23281         * @param {Number} index The index it is being moved to
23282         */
23283        "beforemove" : true,
23284        /**
23285         * @event beforeinsert
23286         * Fires before a new child is inserted, return false to cancel the insert.
23287         * @param {Tree} tree The owner tree
23288         * @param {Node} this This node
23289         * @param {Node} node The child node to be inserted
23290         * @param {Node} refNode The child node the node is being inserted before
23291         */
23292        "beforeinsert" : true
23293    });
23294     this.listeners = this.attributes.listeners;
23295     Roo.data.Node.superclass.constructor.call(this);
23296 };
23297
23298 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23299     fireEvent : function(evtName){
23300         // first do standard event for this node
23301         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23302             return false;
23303         }
23304         // then bubble it up to the tree if the event wasn't cancelled
23305         var ot = this.getOwnerTree();
23306         if(ot){
23307             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23308                 return false;
23309             }
23310         }
23311         return true;
23312     },
23313
23314     /**
23315      * Returns true if this node is a leaf
23316      * @return {Boolean}
23317      */
23318     isLeaf : function(){
23319         return this.leaf === true;
23320     },
23321
23322     // private
23323     setFirstChild : function(node){
23324         this.firstChild = node;
23325     },
23326
23327     //private
23328     setLastChild : function(node){
23329         this.lastChild = node;
23330     },
23331
23332
23333     /**
23334      * Returns true if this node is the last child of its parent
23335      * @return {Boolean}
23336      */
23337     isLast : function(){
23338        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23339     },
23340
23341     /**
23342      * Returns true if this node is the first child of its parent
23343      * @return {Boolean}
23344      */
23345     isFirst : function(){
23346        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23347     },
23348
23349     hasChildNodes : function(){
23350         return !this.isLeaf() && this.childNodes.length > 0;
23351     },
23352
23353     /**
23354      * Insert node(s) as the last child node of this node.
23355      * @param {Node/Array} node The node or Array of nodes to append
23356      * @return {Node} The appended node if single append, or null if an array was passed
23357      */
23358     appendChild : function(node){
23359         var multi = false;
23360         if(node instanceof Array){
23361             multi = node;
23362         }else if(arguments.length > 1){
23363             multi = arguments;
23364         }
23365         // if passed an array or multiple args do them one by one
23366         if(multi){
23367             for(var i = 0, len = multi.length; i < len; i++) {
23368                 this.appendChild(multi[i]);
23369             }
23370         }else{
23371             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23372                 return false;
23373             }
23374             var index = this.childNodes.length;
23375             var oldParent = node.parentNode;
23376             // it's a move, make sure we move it cleanly
23377             if(oldParent){
23378                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23379                     return false;
23380                 }
23381                 oldParent.removeChild(node);
23382             }
23383             index = this.childNodes.length;
23384             if(index == 0){
23385                 this.setFirstChild(node);
23386             }
23387             this.childNodes.push(node);
23388             node.parentNode = this;
23389             var ps = this.childNodes[index-1];
23390             if(ps){
23391                 node.previousSibling = ps;
23392                 ps.nextSibling = node;
23393             }else{
23394                 node.previousSibling = null;
23395             }
23396             node.nextSibling = null;
23397             this.setLastChild(node);
23398             node.setOwnerTree(this.getOwnerTree());
23399             this.fireEvent("append", this.ownerTree, this, node, index);
23400             if(oldParent){
23401                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23402             }
23403             return node;
23404         }
23405     },
23406
23407     /**
23408      * Removes a child node from this node.
23409      * @param {Node} node The node to remove
23410      * @return {Node} The removed node
23411      */
23412     removeChild : function(node){
23413         var index = this.childNodes.indexOf(node);
23414         if(index == -1){
23415             return false;
23416         }
23417         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23418             return false;
23419         }
23420
23421         // remove it from childNodes collection
23422         this.childNodes.splice(index, 1);
23423
23424         // update siblings
23425         if(node.previousSibling){
23426             node.previousSibling.nextSibling = node.nextSibling;
23427         }
23428         if(node.nextSibling){
23429             node.nextSibling.previousSibling = node.previousSibling;
23430         }
23431
23432         // update child refs
23433         if(this.firstChild == node){
23434             this.setFirstChild(node.nextSibling);
23435         }
23436         if(this.lastChild == node){
23437             this.setLastChild(node.previousSibling);
23438         }
23439
23440         node.setOwnerTree(null);
23441         // clear any references from the node
23442         node.parentNode = null;
23443         node.previousSibling = null;
23444         node.nextSibling = null;
23445         this.fireEvent("remove", this.ownerTree, this, node);
23446         return node;
23447     },
23448
23449     /**
23450      * Inserts the first node before the second node in this nodes childNodes collection.
23451      * @param {Node} node The node to insert
23452      * @param {Node} refNode The node to insert before (if null the node is appended)
23453      * @return {Node} The inserted node
23454      */
23455     insertBefore : function(node, refNode){
23456         if(!refNode){ // like standard Dom, refNode can be null for append
23457             return this.appendChild(node);
23458         }
23459         // nothing to do
23460         if(node == refNode){
23461             return false;
23462         }
23463
23464         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23465             return false;
23466         }
23467         var index = this.childNodes.indexOf(refNode);
23468         var oldParent = node.parentNode;
23469         var refIndex = index;
23470
23471         // when moving internally, indexes will change after remove
23472         if(oldParent == this && this.childNodes.indexOf(node) < index){
23473             refIndex--;
23474         }
23475
23476         // it's a move, make sure we move it cleanly
23477         if(oldParent){
23478             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23479                 return false;
23480             }
23481             oldParent.removeChild(node);
23482         }
23483         if(refIndex == 0){
23484             this.setFirstChild(node);
23485         }
23486         this.childNodes.splice(refIndex, 0, node);
23487         node.parentNode = this;
23488         var ps = this.childNodes[refIndex-1];
23489         if(ps){
23490             node.previousSibling = ps;
23491             ps.nextSibling = node;
23492         }else{
23493             node.previousSibling = null;
23494         }
23495         node.nextSibling = refNode;
23496         refNode.previousSibling = node;
23497         node.setOwnerTree(this.getOwnerTree());
23498         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23499         if(oldParent){
23500             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23501         }
23502         return node;
23503     },
23504
23505     /**
23506      * Returns the child node at the specified index.
23507      * @param {Number} index
23508      * @return {Node}
23509      */
23510     item : function(index){
23511         return this.childNodes[index];
23512     },
23513
23514     /**
23515      * Replaces one child node in this node with another.
23516      * @param {Node} newChild The replacement node
23517      * @param {Node} oldChild The node to replace
23518      * @return {Node} The replaced node
23519      */
23520     replaceChild : function(newChild, oldChild){
23521         this.insertBefore(newChild, oldChild);
23522         this.removeChild(oldChild);
23523         return oldChild;
23524     },
23525
23526     /**
23527      * Returns the index of a child node
23528      * @param {Node} node
23529      * @return {Number} The index of the node or -1 if it was not found
23530      */
23531     indexOf : function(child){
23532         return this.childNodes.indexOf(child);
23533     },
23534
23535     /**
23536      * Returns the tree this node is in.
23537      * @return {Tree}
23538      */
23539     getOwnerTree : function(){
23540         // if it doesn't have one, look for one
23541         if(!this.ownerTree){
23542             var p = this;
23543             while(p){
23544                 if(p.ownerTree){
23545                     this.ownerTree = p.ownerTree;
23546                     break;
23547                 }
23548                 p = p.parentNode;
23549             }
23550         }
23551         return this.ownerTree;
23552     },
23553
23554     /**
23555      * Returns depth of this node (the root node has a depth of 0)
23556      * @return {Number}
23557      */
23558     getDepth : function(){
23559         var depth = 0;
23560         var p = this;
23561         while(p.parentNode){
23562             ++depth;
23563             p = p.parentNode;
23564         }
23565         return depth;
23566     },
23567
23568     // private
23569     setOwnerTree : function(tree){
23570         // if it's move, we need to update everyone
23571         if(tree != this.ownerTree){
23572             if(this.ownerTree){
23573                 this.ownerTree.unregisterNode(this);
23574             }
23575             this.ownerTree = tree;
23576             var cs = this.childNodes;
23577             for(var i = 0, len = cs.length; i < len; i++) {
23578                 cs[i].setOwnerTree(tree);
23579             }
23580             if(tree){
23581                 tree.registerNode(this);
23582             }
23583         }
23584     },
23585
23586     /**
23587      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23588      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23589      * @return {String} The path
23590      */
23591     getPath : function(attr){
23592         attr = attr || "id";
23593         var p = this.parentNode;
23594         var b = [this.attributes[attr]];
23595         while(p){
23596             b.unshift(p.attributes[attr]);
23597             p = p.parentNode;
23598         }
23599         var sep = this.getOwnerTree().pathSeparator;
23600         return sep + b.join(sep);
23601     },
23602
23603     /**
23604      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23605      * function call will be the scope provided or the current node. The arguments to the function
23606      * will be the args provided or the current node. If the function returns false at any point,
23607      * the bubble is stopped.
23608      * @param {Function} fn The function to call
23609      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23610      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23611      */
23612     bubble : function(fn, scope, args){
23613         var p = this;
23614         while(p){
23615             if(fn.call(scope || p, args || p) === false){
23616                 break;
23617             }
23618             p = p.parentNode;
23619         }
23620     },
23621
23622     /**
23623      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23624      * function call will be the scope provided or the current node. The arguments to the function
23625      * will be the args provided or the current node. If the function returns false at any point,
23626      * the cascade is stopped on that branch.
23627      * @param {Function} fn The function to call
23628      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23629      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23630      */
23631     cascade : function(fn, scope, args){
23632         if(fn.call(scope || this, args || this) !== false){
23633             var cs = this.childNodes;
23634             for(var i = 0, len = cs.length; i < len; i++) {
23635                 cs[i].cascade(fn, scope, args);
23636             }
23637         }
23638     },
23639
23640     /**
23641      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23642      * function call will be the scope provided or the current node. The arguments to the function
23643      * will be the args provided or the current node. If the function returns false at any point,
23644      * the iteration stops.
23645      * @param {Function} fn The function to call
23646      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23647      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23648      */
23649     eachChild : function(fn, scope, args){
23650         var cs = this.childNodes;
23651         for(var i = 0, len = cs.length; i < len; i++) {
23652                 if(fn.call(scope || this, args || cs[i]) === false){
23653                     break;
23654                 }
23655         }
23656     },
23657
23658     /**
23659      * Finds the first child that has the attribute with the specified value.
23660      * @param {String} attribute The attribute name
23661      * @param {Mixed} value The value to search for
23662      * @return {Node} The found child or null if none was found
23663      */
23664     findChild : function(attribute, value){
23665         var cs = this.childNodes;
23666         for(var i = 0, len = cs.length; i < len; i++) {
23667                 if(cs[i].attributes[attribute] == value){
23668                     return cs[i];
23669                 }
23670         }
23671         return null;
23672     },
23673
23674     /**
23675      * Finds the first child by a custom function. The child matches if the function passed
23676      * returns true.
23677      * @param {Function} fn
23678      * @param {Object} scope (optional)
23679      * @return {Node} The found child or null if none was found
23680      */
23681     findChildBy : function(fn, scope){
23682         var cs = this.childNodes;
23683         for(var i = 0, len = cs.length; i < len; i++) {
23684                 if(fn.call(scope||cs[i], cs[i]) === true){
23685                     return cs[i];
23686                 }
23687         }
23688         return null;
23689     },
23690
23691     /**
23692      * Sorts this nodes children using the supplied sort function
23693      * @param {Function} fn
23694      * @param {Object} scope (optional)
23695      */
23696     sort : function(fn, scope){
23697         var cs = this.childNodes;
23698         var len = cs.length;
23699         if(len > 0){
23700             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23701             cs.sort(sortFn);
23702             for(var i = 0; i < len; i++){
23703                 var n = cs[i];
23704                 n.previousSibling = cs[i-1];
23705                 n.nextSibling = cs[i+1];
23706                 if(i == 0){
23707                     this.setFirstChild(n);
23708                 }
23709                 if(i == len-1){
23710                     this.setLastChild(n);
23711                 }
23712             }
23713         }
23714     },
23715
23716     /**
23717      * Returns true if this node is an ancestor (at any point) of the passed node.
23718      * @param {Node} node
23719      * @return {Boolean}
23720      */
23721     contains : function(node){
23722         return node.isAncestor(this);
23723     },
23724
23725     /**
23726      * Returns true if the passed node is an ancestor (at any point) of this node.
23727      * @param {Node} node
23728      * @return {Boolean}
23729      */
23730     isAncestor : function(node){
23731         var p = this.parentNode;
23732         while(p){
23733             if(p == node){
23734                 return true;
23735             }
23736             p = p.parentNode;
23737         }
23738         return false;
23739     },
23740
23741     toString : function(){
23742         return "[Node"+(this.id?" "+this.id:"")+"]";
23743     }
23744 });/*
23745  * Based on:
23746  * Ext JS Library 1.1.1
23747  * Copyright(c) 2006-2007, Ext JS, LLC.
23748  *
23749  * Originally Released Under LGPL - original licence link has changed is not relivant.
23750  *
23751  * Fork - LGPL
23752  * <script type="text/javascript">
23753  */
23754  (function(){ 
23755 /**
23756  * @class Roo.Layer
23757  * @extends Roo.Element
23758  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23759  * automatic maintaining of shadow/shim positions.
23760  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23761  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23762  * you can pass a string with a CSS class name. False turns off the shadow.
23763  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23764  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23765  * @cfg {String} cls CSS class to add to the element
23766  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23767  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23768  * @constructor
23769  * @param {Object} config An object with config options.
23770  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23771  */
23772
23773 Roo.Layer = function(config, existingEl){
23774     config = config || {};
23775     var dh = Roo.DomHelper;
23776     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23777     if(existingEl){
23778         this.dom = Roo.getDom(existingEl);
23779     }
23780     if(!this.dom){
23781         var o = config.dh || {tag: "div", cls: "x-layer"};
23782         this.dom = dh.append(pel, o);
23783     }
23784     if(config.cls){
23785         this.addClass(config.cls);
23786     }
23787     this.constrain = config.constrain !== false;
23788     this.visibilityMode = Roo.Element.VISIBILITY;
23789     if(config.id){
23790         this.id = this.dom.id = config.id;
23791     }else{
23792         this.id = Roo.id(this.dom);
23793     }
23794     this.zindex = config.zindex || this.getZIndex();
23795     this.position("absolute", this.zindex);
23796     if(config.shadow){
23797         this.shadowOffset = config.shadowOffset || 4;
23798         this.shadow = new Roo.Shadow({
23799             offset : this.shadowOffset,
23800             mode : config.shadow
23801         });
23802     }else{
23803         this.shadowOffset = 0;
23804     }
23805     this.useShim = config.shim !== false && Roo.useShims;
23806     this.useDisplay = config.useDisplay;
23807     this.hide();
23808 };
23809
23810 var supr = Roo.Element.prototype;
23811
23812 // shims are shared among layer to keep from having 100 iframes
23813 var shims = [];
23814
23815 Roo.extend(Roo.Layer, Roo.Element, {
23816
23817     getZIndex : function(){
23818         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23819     },
23820
23821     getShim : function(){
23822         if(!this.useShim){
23823             return null;
23824         }
23825         if(this.shim){
23826             return this.shim;
23827         }
23828         var shim = shims.shift();
23829         if(!shim){
23830             shim = this.createShim();
23831             shim.enableDisplayMode('block');
23832             shim.dom.style.display = 'none';
23833             shim.dom.style.visibility = 'visible';
23834         }
23835         var pn = this.dom.parentNode;
23836         if(shim.dom.parentNode != pn){
23837             pn.insertBefore(shim.dom, this.dom);
23838         }
23839         shim.setStyle('z-index', this.getZIndex()-2);
23840         this.shim = shim;
23841         return shim;
23842     },
23843
23844     hideShim : function(){
23845         if(this.shim){
23846             this.shim.setDisplayed(false);
23847             shims.push(this.shim);
23848             delete this.shim;
23849         }
23850     },
23851
23852     disableShadow : function(){
23853         if(this.shadow){
23854             this.shadowDisabled = true;
23855             this.shadow.hide();
23856             this.lastShadowOffset = this.shadowOffset;
23857             this.shadowOffset = 0;
23858         }
23859     },
23860
23861     enableShadow : function(show){
23862         if(this.shadow){
23863             this.shadowDisabled = false;
23864             this.shadowOffset = this.lastShadowOffset;
23865             delete this.lastShadowOffset;
23866             if(show){
23867                 this.sync(true);
23868             }
23869         }
23870     },
23871
23872     // private
23873     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23874     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23875     sync : function(doShow){
23876         var sw = this.shadow;
23877         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23878             var sh = this.getShim();
23879
23880             var w = this.getWidth(),
23881                 h = this.getHeight();
23882
23883             var l = this.getLeft(true),
23884                 t = this.getTop(true);
23885
23886             if(sw && !this.shadowDisabled){
23887                 if(doShow && !sw.isVisible()){
23888                     sw.show(this);
23889                 }else{
23890                     sw.realign(l, t, w, h);
23891                 }
23892                 if(sh){
23893                     if(doShow){
23894                        sh.show();
23895                     }
23896                     // fit the shim behind the shadow, so it is shimmed too
23897                     var a = sw.adjusts, s = sh.dom.style;
23898                     s.left = (Math.min(l, l+a.l))+"px";
23899                     s.top = (Math.min(t, t+a.t))+"px";
23900                     s.width = (w+a.w)+"px";
23901                     s.height = (h+a.h)+"px";
23902                 }
23903             }else if(sh){
23904                 if(doShow){
23905                    sh.show();
23906                 }
23907                 sh.setSize(w, h);
23908                 sh.setLeftTop(l, t);
23909             }
23910             
23911         }
23912     },
23913
23914     // private
23915     destroy : function(){
23916         this.hideShim();
23917         if(this.shadow){
23918             this.shadow.hide();
23919         }
23920         this.removeAllListeners();
23921         var pn = this.dom.parentNode;
23922         if(pn){
23923             pn.removeChild(this.dom);
23924         }
23925         Roo.Element.uncache(this.id);
23926     },
23927
23928     remove : function(){
23929         this.destroy();
23930     },
23931
23932     // private
23933     beginUpdate : function(){
23934         this.updating = true;
23935     },
23936
23937     // private
23938     endUpdate : function(){
23939         this.updating = false;
23940         this.sync(true);
23941     },
23942
23943     // private
23944     hideUnders : function(negOffset){
23945         if(this.shadow){
23946             this.shadow.hide();
23947         }
23948         this.hideShim();
23949     },
23950
23951     // private
23952     constrainXY : function(){
23953         if(this.constrain){
23954             var vw = Roo.lib.Dom.getViewWidth(),
23955                 vh = Roo.lib.Dom.getViewHeight();
23956             var s = Roo.get(document).getScroll();
23957
23958             var xy = this.getXY();
23959             var x = xy[0], y = xy[1];   
23960             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23961             // only move it if it needs it
23962             var moved = false;
23963             // first validate right/bottom
23964             if((x + w) > vw+s.left){
23965                 x = vw - w - this.shadowOffset;
23966                 moved = true;
23967             }
23968             if((y + h) > vh+s.top){
23969                 y = vh - h - this.shadowOffset;
23970                 moved = true;
23971             }
23972             // then make sure top/left isn't negative
23973             if(x < s.left){
23974                 x = s.left;
23975                 moved = true;
23976             }
23977             if(y < s.top){
23978                 y = s.top;
23979                 moved = true;
23980             }
23981             if(moved){
23982                 if(this.avoidY){
23983                     var ay = this.avoidY;
23984                     if(y <= ay && (y+h) >= ay){
23985                         y = ay-h-5;   
23986                     }
23987                 }
23988                 xy = [x, y];
23989                 this.storeXY(xy);
23990                 supr.setXY.call(this, xy);
23991                 this.sync();
23992             }
23993         }
23994     },
23995
23996     isVisible : function(){
23997         return this.visible;    
23998     },
23999
24000     // private
24001     showAction : function(){
24002         this.visible = true; // track visibility to prevent getStyle calls
24003         if(this.useDisplay === true){
24004             this.setDisplayed("");
24005         }else if(this.lastXY){
24006             supr.setXY.call(this, this.lastXY);
24007         }else if(this.lastLT){
24008             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
24009         }
24010     },
24011
24012     // private
24013     hideAction : function(){
24014         this.visible = false;
24015         if(this.useDisplay === true){
24016             this.setDisplayed(false);
24017         }else{
24018             this.setLeftTop(-10000,-10000);
24019         }
24020     },
24021
24022     // overridden Element method
24023     setVisible : function(v, a, d, c, e){
24024         if(v){
24025             this.showAction();
24026         }
24027         if(a && v){
24028             var cb = function(){
24029                 this.sync(true);
24030                 if(c){
24031                     c();
24032                 }
24033             }.createDelegate(this);
24034             supr.setVisible.call(this, true, true, d, cb, e);
24035         }else{
24036             if(!v){
24037                 this.hideUnders(true);
24038             }
24039             var cb = c;
24040             if(a){
24041                 cb = function(){
24042                     this.hideAction();
24043                     if(c){
24044                         c();
24045                     }
24046                 }.createDelegate(this);
24047             }
24048             supr.setVisible.call(this, v, a, d, cb, e);
24049             if(v){
24050                 this.sync(true);
24051             }else if(!a){
24052                 this.hideAction();
24053             }
24054         }
24055     },
24056
24057     storeXY : function(xy){
24058         delete this.lastLT;
24059         this.lastXY = xy;
24060     },
24061
24062     storeLeftTop : function(left, top){
24063         delete this.lastXY;
24064         this.lastLT = [left, top];
24065     },
24066
24067     // private
24068     beforeFx : function(){
24069         this.beforeAction();
24070         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24071     },
24072
24073     // private
24074     afterFx : function(){
24075         Roo.Layer.superclass.afterFx.apply(this, arguments);
24076         this.sync(this.isVisible());
24077     },
24078
24079     // private
24080     beforeAction : function(){
24081         if(!this.updating && this.shadow){
24082             this.shadow.hide();
24083         }
24084     },
24085
24086     // overridden Element method
24087     setLeft : function(left){
24088         this.storeLeftTop(left, this.getTop(true));
24089         supr.setLeft.apply(this, arguments);
24090         this.sync();
24091     },
24092
24093     setTop : function(top){
24094         this.storeLeftTop(this.getLeft(true), top);
24095         supr.setTop.apply(this, arguments);
24096         this.sync();
24097     },
24098
24099     setLeftTop : function(left, top){
24100         this.storeLeftTop(left, top);
24101         supr.setLeftTop.apply(this, arguments);
24102         this.sync();
24103     },
24104
24105     setXY : function(xy, a, d, c, e){
24106         this.fixDisplay();
24107         this.beforeAction();
24108         this.storeXY(xy);
24109         var cb = this.createCB(c);
24110         supr.setXY.call(this, xy, a, d, cb, e);
24111         if(!a){
24112             cb();
24113         }
24114     },
24115
24116     // private
24117     createCB : function(c){
24118         var el = this;
24119         return function(){
24120             el.constrainXY();
24121             el.sync(true);
24122             if(c){
24123                 c();
24124             }
24125         };
24126     },
24127
24128     // overridden Element method
24129     setX : function(x, a, d, c, e){
24130         this.setXY([x, this.getY()], a, d, c, e);
24131     },
24132
24133     // overridden Element method
24134     setY : function(y, a, d, c, e){
24135         this.setXY([this.getX(), y], a, d, c, e);
24136     },
24137
24138     // overridden Element method
24139     setSize : function(w, h, a, d, c, e){
24140         this.beforeAction();
24141         var cb = this.createCB(c);
24142         supr.setSize.call(this, w, h, a, d, cb, e);
24143         if(!a){
24144             cb();
24145         }
24146     },
24147
24148     // overridden Element method
24149     setWidth : function(w, a, d, c, e){
24150         this.beforeAction();
24151         var cb = this.createCB(c);
24152         supr.setWidth.call(this, w, a, d, cb, e);
24153         if(!a){
24154             cb();
24155         }
24156     },
24157
24158     // overridden Element method
24159     setHeight : function(h, a, d, c, e){
24160         this.beforeAction();
24161         var cb = this.createCB(c);
24162         supr.setHeight.call(this, h, a, d, cb, e);
24163         if(!a){
24164             cb();
24165         }
24166     },
24167
24168     // overridden Element method
24169     setBounds : function(x, y, w, h, a, d, c, e){
24170         this.beforeAction();
24171         var cb = this.createCB(c);
24172         if(!a){
24173             this.storeXY([x, y]);
24174             supr.setXY.call(this, [x, y]);
24175             supr.setSize.call(this, w, h, a, d, cb, e);
24176             cb();
24177         }else{
24178             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24179         }
24180         return this;
24181     },
24182     
24183     /**
24184      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24185      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24186      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24187      * @param {Number} zindex The new z-index to set
24188      * @return {this} The Layer
24189      */
24190     setZIndex : function(zindex){
24191         this.zindex = zindex;
24192         this.setStyle("z-index", zindex + 2);
24193         if(this.shadow){
24194             this.shadow.setZIndex(zindex + 1);
24195         }
24196         if(this.shim){
24197             this.shim.setStyle("z-index", zindex);
24198         }
24199     }
24200 });
24201 })();/*
24202  * Based on:
24203  * Ext JS Library 1.1.1
24204  * Copyright(c) 2006-2007, Ext JS, LLC.
24205  *
24206  * Originally Released Under LGPL - original licence link has changed is not relivant.
24207  *
24208  * Fork - LGPL
24209  * <script type="text/javascript">
24210  */
24211
24212
24213 /**
24214  * @class Roo.Shadow
24215  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24216  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24217  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24218  * @constructor
24219  * Create a new Shadow
24220  * @param {Object} config The config object
24221  */
24222 Roo.Shadow = function(config){
24223     Roo.apply(this, config);
24224     if(typeof this.mode != "string"){
24225         this.mode = this.defaultMode;
24226     }
24227     var o = this.offset, a = {h: 0};
24228     var rad = Math.floor(this.offset/2);
24229     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24230         case "drop":
24231             a.w = 0;
24232             a.l = a.t = o;
24233             a.t -= 1;
24234             if(Roo.isIE){
24235                 a.l -= this.offset + rad;
24236                 a.t -= this.offset + rad;
24237                 a.w -= rad;
24238                 a.h -= rad;
24239                 a.t += 1;
24240             }
24241         break;
24242         case "sides":
24243             a.w = (o*2);
24244             a.l = -o;
24245             a.t = o-1;
24246             if(Roo.isIE){
24247                 a.l -= (this.offset - rad);
24248                 a.t -= this.offset + rad;
24249                 a.l += 1;
24250                 a.w -= (this.offset - rad)*2;
24251                 a.w -= rad + 1;
24252                 a.h -= 1;
24253             }
24254         break;
24255         case "frame":
24256             a.w = a.h = (o*2);
24257             a.l = a.t = -o;
24258             a.t += 1;
24259             a.h -= 2;
24260             if(Roo.isIE){
24261                 a.l -= (this.offset - rad);
24262                 a.t -= (this.offset - rad);
24263                 a.l += 1;
24264                 a.w -= (this.offset + rad + 1);
24265                 a.h -= (this.offset + rad);
24266                 a.h += 1;
24267             }
24268         break;
24269     };
24270
24271     this.adjusts = a;
24272 };
24273
24274 Roo.Shadow.prototype = {
24275     /**
24276      * @cfg {String} mode
24277      * The shadow display mode.  Supports the following options:<br />
24278      * sides: Shadow displays on both sides and bottom only<br />
24279      * frame: Shadow displays equally on all four sides<br />
24280      * drop: Traditional bottom-right drop shadow (default)
24281      */
24282     /**
24283      * @cfg {String} offset
24284      * The number of pixels to offset the shadow from the element (defaults to 4)
24285      */
24286     offset: 4,
24287
24288     // private
24289     defaultMode: "drop",
24290
24291     /**
24292      * Displays the shadow under the target element
24293      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24294      */
24295     show : function(target){
24296         target = Roo.get(target);
24297         if(!this.el){
24298             this.el = Roo.Shadow.Pool.pull();
24299             if(this.el.dom.nextSibling != target.dom){
24300                 this.el.insertBefore(target);
24301             }
24302         }
24303         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24304         if(Roo.isIE){
24305             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24306         }
24307         this.realign(
24308             target.getLeft(true),
24309             target.getTop(true),
24310             target.getWidth(),
24311             target.getHeight()
24312         );
24313         this.el.dom.style.display = "block";
24314     },
24315
24316     /**
24317      * Returns true if the shadow is visible, else false
24318      */
24319     isVisible : function(){
24320         return this.el ? true : false;  
24321     },
24322
24323     /**
24324      * Direct alignment when values are already available. Show must be called at least once before
24325      * calling this method to ensure it is initialized.
24326      * @param {Number} left The target element left position
24327      * @param {Number} top The target element top position
24328      * @param {Number} width The target element width
24329      * @param {Number} height The target element height
24330      */
24331     realign : function(l, t, w, h){
24332         if(!this.el){
24333             return;
24334         }
24335         var a = this.adjusts, d = this.el.dom, s = d.style;
24336         var iea = 0;
24337         s.left = (l+a.l)+"px";
24338         s.top = (t+a.t)+"px";
24339         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24340  
24341         if(s.width != sws || s.height != shs){
24342             s.width = sws;
24343             s.height = shs;
24344             if(!Roo.isIE){
24345                 var cn = d.childNodes;
24346                 var sww = Math.max(0, (sw-12))+"px";
24347                 cn[0].childNodes[1].style.width = sww;
24348                 cn[1].childNodes[1].style.width = sww;
24349                 cn[2].childNodes[1].style.width = sww;
24350                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24351             }
24352         }
24353     },
24354
24355     /**
24356      * Hides this shadow
24357      */
24358     hide : function(){
24359         if(this.el){
24360             this.el.dom.style.display = "none";
24361             Roo.Shadow.Pool.push(this.el);
24362             delete this.el;
24363         }
24364     },
24365
24366     /**
24367      * Adjust the z-index of this shadow
24368      * @param {Number} zindex The new z-index
24369      */
24370     setZIndex : function(z){
24371         this.zIndex = z;
24372         if(this.el){
24373             this.el.setStyle("z-index", z);
24374         }
24375     }
24376 };
24377
24378 // Private utility class that manages the internal Shadow cache
24379 Roo.Shadow.Pool = function(){
24380     var p = [];
24381     var markup = Roo.isIE ?
24382                  '<div class="x-ie-shadow"></div>' :
24383                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
24384     return {
24385         pull : function(){
24386             var sh = p.shift();
24387             if(!sh){
24388                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24389                 sh.autoBoxAdjust = false;
24390             }
24391             return sh;
24392         },
24393
24394         push : function(sh){
24395             p.push(sh);
24396         }
24397     };
24398 }();/*
24399  * Based on:
24400  * Ext JS Library 1.1.1
24401  * Copyright(c) 2006-2007, Ext JS, LLC.
24402  *
24403  * Originally Released Under LGPL - original licence link has changed is not relivant.
24404  *
24405  * Fork - LGPL
24406  * <script type="text/javascript">
24407  */
24408
24409
24410 /**
24411  * @class Roo.SplitBar
24412  * @extends Roo.util.Observable
24413  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24414  * <br><br>
24415  * Usage:
24416  * <pre><code>
24417 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24418                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24419 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24420 split.minSize = 100;
24421 split.maxSize = 600;
24422 split.animate = true;
24423 split.on('moved', splitterMoved);
24424 </code></pre>
24425  * @constructor
24426  * Create a new SplitBar
24427  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24428  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24429  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24430  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24431                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24432                         position of the SplitBar).
24433  */
24434 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24435     
24436     /** @private */
24437     this.el = Roo.get(dragElement, true);
24438     this.el.dom.unselectable = "on";
24439     /** @private */
24440     this.resizingEl = Roo.get(resizingElement, true);
24441
24442     /**
24443      * @private
24444      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24445      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24446      * @type Number
24447      */
24448     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24449     
24450     /**
24451      * The minimum size of the resizing element. (Defaults to 0)
24452      * @type Number
24453      */
24454     this.minSize = 0;
24455     
24456     /**
24457      * The maximum size of the resizing element. (Defaults to 2000)
24458      * @type Number
24459      */
24460     this.maxSize = 2000;
24461     
24462     /**
24463      * Whether to animate the transition to the new size
24464      * @type Boolean
24465      */
24466     this.animate = false;
24467     
24468     /**
24469      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24470      * @type Boolean
24471      */
24472     this.useShim = false;
24473     
24474     /** @private */
24475     this.shim = null;
24476     
24477     if(!existingProxy){
24478         /** @private */
24479         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24480     }else{
24481         this.proxy = Roo.get(existingProxy).dom;
24482     }
24483     /** @private */
24484     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24485     
24486     /** @private */
24487     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24488     
24489     /** @private */
24490     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24491     
24492     /** @private */
24493     this.dragSpecs = {};
24494     
24495     /**
24496      * @private The adapter to use to positon and resize elements
24497      */
24498     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24499     this.adapter.init(this);
24500     
24501     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24502         /** @private */
24503         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24504         this.el.addClass("x-splitbar-h");
24505     }else{
24506         /** @private */
24507         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24508         this.el.addClass("x-splitbar-v");
24509     }
24510     
24511     this.addEvents({
24512         /**
24513          * @event resize
24514          * Fires when the splitter is moved (alias for {@link #event-moved})
24515          * @param {Roo.SplitBar} this
24516          * @param {Number} newSize the new width or height
24517          */
24518         "resize" : true,
24519         /**
24520          * @event moved
24521          * Fires when the splitter is moved
24522          * @param {Roo.SplitBar} this
24523          * @param {Number} newSize the new width or height
24524          */
24525         "moved" : true,
24526         /**
24527          * @event beforeresize
24528          * Fires before the splitter is dragged
24529          * @param {Roo.SplitBar} this
24530          */
24531         "beforeresize" : true,
24532
24533         "beforeapply" : true
24534     });
24535
24536     Roo.util.Observable.call(this);
24537 };
24538
24539 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24540     onStartProxyDrag : function(x, y){
24541         this.fireEvent("beforeresize", this);
24542         if(!this.overlay){
24543             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24544             o.unselectable();
24545             o.enableDisplayMode("block");
24546             // all splitbars share the same overlay
24547             Roo.SplitBar.prototype.overlay = o;
24548         }
24549         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24550         this.overlay.show();
24551         Roo.get(this.proxy).setDisplayed("block");
24552         var size = this.adapter.getElementSize(this);
24553         this.activeMinSize = this.getMinimumSize();;
24554         this.activeMaxSize = this.getMaximumSize();;
24555         var c1 = size - this.activeMinSize;
24556         var c2 = Math.max(this.activeMaxSize - size, 0);
24557         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24558             this.dd.resetConstraints();
24559             this.dd.setXConstraint(
24560                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24561                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24562             );
24563             this.dd.setYConstraint(0, 0);
24564         }else{
24565             this.dd.resetConstraints();
24566             this.dd.setXConstraint(0, 0);
24567             this.dd.setYConstraint(
24568                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24569                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24570             );
24571          }
24572         this.dragSpecs.startSize = size;
24573         this.dragSpecs.startPoint = [x, y];
24574         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24575     },
24576     
24577     /** 
24578      * @private Called after the drag operation by the DDProxy
24579      */
24580     onEndProxyDrag : function(e){
24581         Roo.get(this.proxy).setDisplayed(false);
24582         var endPoint = Roo.lib.Event.getXY(e);
24583         if(this.overlay){
24584             this.overlay.hide();
24585         }
24586         var newSize;
24587         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24588             newSize = this.dragSpecs.startSize + 
24589                 (this.placement == Roo.SplitBar.LEFT ?
24590                     endPoint[0] - this.dragSpecs.startPoint[0] :
24591                     this.dragSpecs.startPoint[0] - endPoint[0]
24592                 );
24593         }else{
24594             newSize = this.dragSpecs.startSize + 
24595                 (this.placement == Roo.SplitBar.TOP ?
24596                     endPoint[1] - this.dragSpecs.startPoint[1] :
24597                     this.dragSpecs.startPoint[1] - endPoint[1]
24598                 );
24599         }
24600         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24601         if(newSize != this.dragSpecs.startSize){
24602             if(this.fireEvent('beforeapply', this, newSize) !== false){
24603                 this.adapter.setElementSize(this, newSize);
24604                 this.fireEvent("moved", this, newSize);
24605                 this.fireEvent("resize", this, newSize);
24606             }
24607         }
24608     },
24609     
24610     /**
24611      * Get the adapter this SplitBar uses
24612      * @return The adapter object
24613      */
24614     getAdapter : function(){
24615         return this.adapter;
24616     },
24617     
24618     /**
24619      * Set the adapter this SplitBar uses
24620      * @param {Object} adapter A SplitBar adapter object
24621      */
24622     setAdapter : function(adapter){
24623         this.adapter = adapter;
24624         this.adapter.init(this);
24625     },
24626     
24627     /**
24628      * Gets the minimum size for the resizing element
24629      * @return {Number} The minimum size
24630      */
24631     getMinimumSize : function(){
24632         return this.minSize;
24633     },
24634     
24635     /**
24636      * Sets the minimum size for the resizing element
24637      * @param {Number} minSize The minimum size
24638      */
24639     setMinimumSize : function(minSize){
24640         this.minSize = minSize;
24641     },
24642     
24643     /**
24644      * Gets the maximum size for the resizing element
24645      * @return {Number} The maximum size
24646      */
24647     getMaximumSize : function(){
24648         return this.maxSize;
24649     },
24650     
24651     /**
24652      * Sets the maximum size for the resizing element
24653      * @param {Number} maxSize The maximum size
24654      */
24655     setMaximumSize : function(maxSize){
24656         this.maxSize = maxSize;
24657     },
24658     
24659     /**
24660      * Sets the initialize size for the resizing element
24661      * @param {Number} size The initial size
24662      */
24663     setCurrentSize : function(size){
24664         var oldAnimate = this.animate;
24665         this.animate = false;
24666         this.adapter.setElementSize(this, size);
24667         this.animate = oldAnimate;
24668     },
24669     
24670     /**
24671      * Destroy this splitbar. 
24672      * @param {Boolean} removeEl True to remove the element
24673      */
24674     destroy : function(removeEl){
24675         if(this.shim){
24676             this.shim.remove();
24677         }
24678         this.dd.unreg();
24679         this.proxy.parentNode.removeChild(this.proxy);
24680         if(removeEl){
24681             this.el.remove();
24682         }
24683     }
24684 });
24685
24686 /**
24687  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
24688  */
24689 Roo.SplitBar.createProxy = function(dir){
24690     var proxy = new Roo.Element(document.createElement("div"));
24691     proxy.unselectable();
24692     var cls = 'x-splitbar-proxy';
24693     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24694     document.body.appendChild(proxy.dom);
24695     return proxy.dom;
24696 };
24697
24698 /** 
24699  * @class Roo.SplitBar.BasicLayoutAdapter
24700  * Default Adapter. It assumes the splitter and resizing element are not positioned
24701  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24702  */
24703 Roo.SplitBar.BasicLayoutAdapter = function(){
24704 };
24705
24706 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24707     // do nothing for now
24708     init : function(s){
24709     
24710     },
24711     /**
24712      * Called before drag operations to get the current size of the resizing element. 
24713      * @param {Roo.SplitBar} s The SplitBar using this adapter
24714      */
24715      getElementSize : function(s){
24716         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24717             return s.resizingEl.getWidth();
24718         }else{
24719             return s.resizingEl.getHeight();
24720         }
24721     },
24722     
24723     /**
24724      * Called after drag operations to set the size of the resizing element.
24725      * @param {Roo.SplitBar} s The SplitBar using this adapter
24726      * @param {Number} newSize The new size to set
24727      * @param {Function} onComplete A function to be invoked when resizing is complete
24728      */
24729     setElementSize : function(s, newSize, onComplete){
24730         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24731             if(!s.animate){
24732                 s.resizingEl.setWidth(newSize);
24733                 if(onComplete){
24734                     onComplete(s, newSize);
24735                 }
24736             }else{
24737                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24738             }
24739         }else{
24740             
24741             if(!s.animate){
24742                 s.resizingEl.setHeight(newSize);
24743                 if(onComplete){
24744                     onComplete(s, newSize);
24745                 }
24746             }else{
24747                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24748             }
24749         }
24750     }
24751 };
24752
24753 /** 
24754  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24755  * @extends Roo.SplitBar.BasicLayoutAdapter
24756  * Adapter that  moves the splitter element to align with the resized sizing element. 
24757  * Used with an absolute positioned SplitBar.
24758  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24759  * document.body, make sure you assign an id to the body element.
24760  */
24761 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24762     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24763     this.container = Roo.get(container);
24764 };
24765
24766 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24767     init : function(s){
24768         this.basic.init(s);
24769     },
24770     
24771     getElementSize : function(s){
24772         return this.basic.getElementSize(s);
24773     },
24774     
24775     setElementSize : function(s, newSize, onComplete){
24776         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24777     },
24778     
24779     moveSplitter : function(s){
24780         var yes = Roo.SplitBar;
24781         switch(s.placement){
24782             case yes.LEFT:
24783                 s.el.setX(s.resizingEl.getRight());
24784                 break;
24785             case yes.RIGHT:
24786                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24787                 break;
24788             case yes.TOP:
24789                 s.el.setY(s.resizingEl.getBottom());
24790                 break;
24791             case yes.BOTTOM:
24792                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24793                 break;
24794         }
24795     }
24796 };
24797
24798 /**
24799  * Orientation constant - Create a vertical SplitBar
24800  * @static
24801  * @type Number
24802  */
24803 Roo.SplitBar.VERTICAL = 1;
24804
24805 /**
24806  * Orientation constant - Create a horizontal SplitBar
24807  * @static
24808  * @type Number
24809  */
24810 Roo.SplitBar.HORIZONTAL = 2;
24811
24812 /**
24813  * Placement constant - The resizing element is to the left of the splitter element
24814  * @static
24815  * @type Number
24816  */
24817 Roo.SplitBar.LEFT = 1;
24818
24819 /**
24820  * Placement constant - The resizing element is to the right of the splitter element
24821  * @static
24822  * @type Number
24823  */
24824 Roo.SplitBar.RIGHT = 2;
24825
24826 /**
24827  * Placement constant - The resizing element is positioned above the splitter element
24828  * @static
24829  * @type Number
24830  */
24831 Roo.SplitBar.TOP = 3;
24832
24833 /**
24834  * Placement constant - The resizing element is positioned under splitter element
24835  * @static
24836  * @type Number
24837  */
24838 Roo.SplitBar.BOTTOM = 4;
24839 /*
24840  * Based on:
24841  * Ext JS Library 1.1.1
24842  * Copyright(c) 2006-2007, Ext JS, LLC.
24843  *
24844  * Originally Released Under LGPL - original licence link has changed is not relivant.
24845  *
24846  * Fork - LGPL
24847  * <script type="text/javascript">
24848  */
24849
24850 /**
24851  * @class Roo.View
24852  * @extends Roo.util.Observable
24853  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24854  * This class also supports single and multi selection modes. <br>
24855  * Create a data model bound view:
24856  <pre><code>
24857  var store = new Roo.data.Store(...);
24858
24859  var view = new Roo.View({
24860     el : "my-element",
24861     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24862  
24863     singleSelect: true,
24864     selectedClass: "ydataview-selected",
24865     store: store
24866  });
24867
24868  // listen for node click?
24869  view.on("click", function(vw, index, node, e){
24870  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24871  });
24872
24873  // load XML data
24874  dataModel.load("foobar.xml");
24875  </code></pre>
24876  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24877  * <br><br>
24878  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24879  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24880  * 
24881  * Note: old style constructor is still suported (container, template, config)
24882  * 
24883  * @constructor
24884  * Create a new View
24885  * @param {Object} config The config object
24886  * 
24887  */
24888 Roo.View = function(config, depreciated_tpl, depreciated_config){
24889     
24890     this.parent = false;
24891     
24892     if (typeof(depreciated_tpl) == 'undefined') {
24893         // new way.. - universal constructor.
24894         Roo.apply(this, config);
24895         this.el  = Roo.get(this.el);
24896     } else {
24897         // old format..
24898         this.el  = Roo.get(config);
24899         this.tpl = depreciated_tpl;
24900         Roo.apply(this, depreciated_config);
24901     }
24902     this.wrapEl  = this.el.wrap().wrap();
24903     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24904     
24905     
24906     if(typeof(this.tpl) == "string"){
24907         this.tpl = new Roo.Template(this.tpl);
24908     } else {
24909         // support xtype ctors..
24910         this.tpl = new Roo.factory(this.tpl, Roo);
24911     }
24912     
24913     
24914     this.tpl.compile();
24915     
24916     /** @private */
24917     this.addEvents({
24918         /**
24919          * @event beforeclick
24920          * Fires before a click is processed. Returns false to cancel the default action.
24921          * @param {Roo.View} this
24922          * @param {Number} index The index of the target node
24923          * @param {HTMLElement} node The target node
24924          * @param {Roo.EventObject} e The raw event object
24925          */
24926             "beforeclick" : true,
24927         /**
24928          * @event click
24929          * Fires when a template node is clicked.
24930          * @param {Roo.View} this
24931          * @param {Number} index The index of the target node
24932          * @param {HTMLElement} node The target node
24933          * @param {Roo.EventObject} e The raw event object
24934          */
24935             "click" : true,
24936         /**
24937          * @event dblclick
24938          * Fires when a template node is double clicked.
24939          * @param {Roo.View} this
24940          * @param {Number} index The index of the target node
24941          * @param {HTMLElement} node The target node
24942          * @param {Roo.EventObject} e The raw event object
24943          */
24944             "dblclick" : true,
24945         /**
24946          * @event contextmenu
24947          * Fires when a template node is right clicked.
24948          * @param {Roo.View} this
24949          * @param {Number} index The index of the target node
24950          * @param {HTMLElement} node The target node
24951          * @param {Roo.EventObject} e The raw event object
24952          */
24953             "contextmenu" : true,
24954         /**
24955          * @event selectionchange
24956          * Fires when the selected nodes change.
24957          * @param {Roo.View} this
24958          * @param {Array} selections Array of the selected nodes
24959          */
24960             "selectionchange" : true,
24961     
24962         /**
24963          * @event beforeselect
24964          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24965          * @param {Roo.View} this
24966          * @param {HTMLElement} node The node to be selected
24967          * @param {Array} selections Array of currently selected nodes
24968          */
24969             "beforeselect" : true,
24970         /**
24971          * @event preparedata
24972          * Fires on every row to render, to allow you to change the data.
24973          * @param {Roo.View} this
24974          * @param {Object} data to be rendered (change this)
24975          */
24976           "preparedata" : true
24977           
24978           
24979         });
24980
24981
24982
24983     this.el.on({
24984         "click": this.onClick,
24985         "dblclick": this.onDblClick,
24986         "contextmenu": this.onContextMenu,
24987         scope:this
24988     });
24989
24990     this.selections = [];
24991     this.nodes = [];
24992     this.cmp = new Roo.CompositeElementLite([]);
24993     if(this.store){
24994         this.store = Roo.factory(this.store, Roo.data);
24995         this.setStore(this.store, true);
24996     }
24997     
24998     if ( this.footer && this.footer.xtype) {
24999            
25000          var fctr = this.wrapEl.appendChild(document.createElement("div"));
25001         
25002         this.footer.dataSource = this.store;
25003         this.footer.container = fctr;
25004         this.footer = Roo.factory(this.footer, Roo);
25005         fctr.insertFirst(this.el);
25006         
25007         // this is a bit insane - as the paging toolbar seems to detach the el..
25008 //        dom.parentNode.parentNode.parentNode
25009          // they get detached?
25010     }
25011     
25012     
25013     Roo.View.superclass.constructor.call(this);
25014     
25015     
25016 };
25017
25018 Roo.extend(Roo.View, Roo.util.Observable, {
25019     
25020      /**
25021      * @cfg {Roo.data.Store} store Data store to load data from.
25022      */
25023     store : false,
25024     
25025     /**
25026      * @cfg {String|Roo.Element} el The container element.
25027      */
25028     el : '',
25029     
25030     /**
25031      * @cfg {String|Roo.Template} tpl The template used by this View 
25032      */
25033     tpl : false,
25034     /**
25035      * @cfg {String} dataName the named area of the template to use as the data area
25036      *                          Works with domtemplates roo-name="name"
25037      */
25038     dataName: false,
25039     /**
25040      * @cfg {String} selectedClass The css class to add to selected nodes
25041      */
25042     selectedClass : "x-view-selected",
25043      /**
25044      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25045      */
25046     emptyText : "",
25047     
25048     /**
25049      * @cfg {String} text to display on mask (default Loading)
25050      */
25051     mask : false,
25052     /**
25053      * @cfg {Boolean} multiSelect Allow multiple selection
25054      */
25055     multiSelect : false,
25056     /**
25057      * @cfg {Boolean} singleSelect Allow single selection
25058      */
25059     singleSelect:  false,
25060     
25061     /**
25062      * @cfg {Boolean} toggleSelect - selecting 
25063      */
25064     toggleSelect : false,
25065     
25066     /**
25067      * @cfg {Boolean} tickable - selecting 
25068      */
25069     tickable : false,
25070     
25071     /**
25072      * Returns the element this view is bound to.
25073      * @return {Roo.Element}
25074      */
25075     getEl : function(){
25076         return this.wrapEl;
25077     },
25078     
25079     
25080
25081     /**
25082      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25083      */
25084     refresh : function(){
25085         //Roo.log('refresh');
25086         var t = this.tpl;
25087         
25088         // if we are using something like 'domtemplate', then
25089         // the what gets used is:
25090         // t.applySubtemplate(NAME, data, wrapping data..)
25091         // the outer template then get' applied with
25092         //     the store 'extra data'
25093         // and the body get's added to the
25094         //      roo-name="data" node?
25095         //      <span class='roo-tpl-{name}'></span> ?????
25096         
25097         
25098         
25099         this.clearSelections();
25100         this.el.update("");
25101         var html = [];
25102         var records = this.store.getRange();
25103         if(records.length < 1) {
25104             
25105             // is this valid??  = should it render a template??
25106             
25107             this.el.update(this.emptyText);
25108             return;
25109         }
25110         var el = this.el;
25111         if (this.dataName) {
25112             this.el.update(t.apply(this.store.meta)); //????
25113             el = this.el.child('.roo-tpl-' + this.dataName);
25114         }
25115         
25116         for(var i = 0, len = records.length; i < len; i++){
25117             var data = this.prepareData(records[i].data, i, records[i]);
25118             this.fireEvent("preparedata", this, data, i, records[i]);
25119             
25120             var d = Roo.apply({}, data);
25121             
25122             if(this.tickable){
25123                 Roo.apply(d, {'roo-id' : Roo.id()});
25124                 
25125                 var _this = this;
25126             
25127                 Roo.each(this.parent.item, function(item){
25128                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25129                         return;
25130                     }
25131                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25132                 });
25133             }
25134             
25135             html[html.length] = Roo.util.Format.trim(
25136                 this.dataName ?
25137                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25138                     t.apply(d)
25139             );
25140         }
25141         
25142         
25143         
25144         el.update(html.join(""));
25145         this.nodes = el.dom.childNodes;
25146         this.updateIndexes(0);
25147     },
25148     
25149
25150     /**
25151      * Function to override to reformat the data that is sent to
25152      * the template for each node.
25153      * DEPRICATED - use the preparedata event handler.
25154      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25155      * a JSON object for an UpdateManager bound view).
25156      */
25157     prepareData : function(data, index, record)
25158     {
25159         this.fireEvent("preparedata", this, data, index, record);
25160         return data;
25161     },
25162
25163     onUpdate : function(ds, record){
25164         // Roo.log('on update');   
25165         this.clearSelections();
25166         var index = this.store.indexOf(record);
25167         var n = this.nodes[index];
25168         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25169         n.parentNode.removeChild(n);
25170         this.updateIndexes(index, index);
25171     },
25172
25173     
25174     
25175 // --------- FIXME     
25176     onAdd : function(ds, records, index)
25177     {
25178         //Roo.log(['on Add', ds, records, index] );        
25179         this.clearSelections();
25180         if(this.nodes.length == 0){
25181             this.refresh();
25182             return;
25183         }
25184         var n = this.nodes[index];
25185         for(var i = 0, len = records.length; i < len; i++){
25186             var d = this.prepareData(records[i].data, i, records[i]);
25187             if(n){
25188                 this.tpl.insertBefore(n, d);
25189             }else{
25190                 
25191                 this.tpl.append(this.el, d);
25192             }
25193         }
25194         this.updateIndexes(index);
25195     },
25196
25197     onRemove : function(ds, record, index){
25198        // Roo.log('onRemove');
25199         this.clearSelections();
25200         var el = this.dataName  ?
25201             this.el.child('.roo-tpl-' + this.dataName) :
25202             this.el; 
25203         
25204         el.dom.removeChild(this.nodes[index]);
25205         this.updateIndexes(index);
25206     },
25207
25208     /**
25209      * Refresh an individual node.
25210      * @param {Number} index
25211      */
25212     refreshNode : function(index){
25213         this.onUpdate(this.store, this.store.getAt(index));
25214     },
25215
25216     updateIndexes : function(startIndex, endIndex){
25217         var ns = this.nodes;
25218         startIndex = startIndex || 0;
25219         endIndex = endIndex || ns.length - 1;
25220         for(var i = startIndex; i <= endIndex; i++){
25221             ns[i].nodeIndex = i;
25222         }
25223     },
25224
25225     /**
25226      * Changes the data store this view uses and refresh the view.
25227      * @param {Store} store
25228      */
25229     setStore : function(store, initial){
25230         if(!initial && this.store){
25231             this.store.un("datachanged", this.refresh);
25232             this.store.un("add", this.onAdd);
25233             this.store.un("remove", this.onRemove);
25234             this.store.un("update", this.onUpdate);
25235             this.store.un("clear", this.refresh);
25236             this.store.un("beforeload", this.onBeforeLoad);
25237             this.store.un("load", this.onLoad);
25238             this.store.un("loadexception", this.onLoad);
25239         }
25240         if(store){
25241           
25242             store.on("datachanged", this.refresh, this);
25243             store.on("add", this.onAdd, this);
25244             store.on("remove", this.onRemove, this);
25245             store.on("update", this.onUpdate, this);
25246             store.on("clear", this.refresh, this);
25247             store.on("beforeload", this.onBeforeLoad, this);
25248             store.on("load", this.onLoad, this);
25249             store.on("loadexception", this.onLoad, this);
25250         }
25251         
25252         if(store){
25253             this.refresh();
25254         }
25255     },
25256     /**
25257      * onbeforeLoad - masks the loading area.
25258      *
25259      */
25260     onBeforeLoad : function(store,opts)
25261     {
25262          //Roo.log('onBeforeLoad');   
25263         if (!opts.add) {
25264             this.el.update("");
25265         }
25266         this.el.mask(this.mask ? this.mask : "Loading" ); 
25267     },
25268     onLoad : function ()
25269     {
25270         this.el.unmask();
25271     },
25272     
25273
25274     /**
25275      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25276      * @param {HTMLElement} node
25277      * @return {HTMLElement} The template node
25278      */
25279     findItemFromChild : function(node){
25280         var el = this.dataName  ?
25281             this.el.child('.roo-tpl-' + this.dataName,true) :
25282             this.el.dom; 
25283         
25284         if(!node || node.parentNode == el){
25285                     return node;
25286             }
25287             var p = node.parentNode;
25288             while(p && p != el){
25289             if(p.parentNode == el){
25290                 return p;
25291             }
25292             p = p.parentNode;
25293         }
25294             return null;
25295     },
25296
25297     /** @ignore */
25298     onClick : function(e){
25299         var item = this.findItemFromChild(e.getTarget());
25300         if(item){
25301             var index = this.indexOf(item);
25302             if(this.onItemClick(item, index, e) !== false){
25303                 this.fireEvent("click", this, index, item, e);
25304             }
25305         }else{
25306             this.clearSelections();
25307         }
25308     },
25309
25310     /** @ignore */
25311     onContextMenu : function(e){
25312         var item = this.findItemFromChild(e.getTarget());
25313         if(item){
25314             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25315         }
25316     },
25317
25318     /** @ignore */
25319     onDblClick : function(e){
25320         var item = this.findItemFromChild(e.getTarget());
25321         if(item){
25322             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25323         }
25324     },
25325
25326     onItemClick : function(item, index, e)
25327     {
25328         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25329             return false;
25330         }
25331         if (this.toggleSelect) {
25332             var m = this.isSelected(item) ? 'unselect' : 'select';
25333             //Roo.log(m);
25334             var _t = this;
25335             _t[m](item, true, false);
25336             return true;
25337         }
25338         if(this.multiSelect || this.singleSelect){
25339             if(this.multiSelect && e.shiftKey && this.lastSelection){
25340                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25341             }else{
25342                 this.select(item, this.multiSelect && e.ctrlKey);
25343                 this.lastSelection = item;
25344             }
25345             
25346             if(!this.tickable){
25347                 e.preventDefault();
25348             }
25349             
25350         }
25351         return true;
25352     },
25353
25354     /**
25355      * Get the number of selected nodes.
25356      * @return {Number}
25357      */
25358     getSelectionCount : function(){
25359         return this.selections.length;
25360     },
25361
25362     /**
25363      * Get the currently selected nodes.
25364      * @return {Array} An array of HTMLElements
25365      */
25366     getSelectedNodes : function(){
25367         return this.selections;
25368     },
25369
25370     /**
25371      * Get the indexes of the selected nodes.
25372      * @return {Array}
25373      */
25374     getSelectedIndexes : function(){
25375         var indexes = [], s = this.selections;
25376         for(var i = 0, len = s.length; i < len; i++){
25377             indexes.push(s[i].nodeIndex);
25378         }
25379         return indexes;
25380     },
25381
25382     /**
25383      * Clear all selections
25384      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25385      */
25386     clearSelections : function(suppressEvent){
25387         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25388             this.cmp.elements = this.selections;
25389             this.cmp.removeClass(this.selectedClass);
25390             this.selections = [];
25391             if(!suppressEvent){
25392                 this.fireEvent("selectionchange", this, this.selections);
25393             }
25394         }
25395     },
25396
25397     /**
25398      * Returns true if the passed node is selected
25399      * @param {HTMLElement/Number} node The node or node index
25400      * @return {Boolean}
25401      */
25402     isSelected : function(node){
25403         var s = this.selections;
25404         if(s.length < 1){
25405             return false;
25406         }
25407         node = this.getNode(node);
25408         return s.indexOf(node) !== -1;
25409     },
25410
25411     /**
25412      * Selects nodes.
25413      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
25414      * @param {Boolean} keepExisting (optional) true to keep existing selections
25415      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25416      */
25417     select : function(nodeInfo, keepExisting, suppressEvent){
25418         if(nodeInfo instanceof Array){
25419             if(!keepExisting){
25420                 this.clearSelections(true);
25421             }
25422             for(var i = 0, len = nodeInfo.length; i < len; i++){
25423                 this.select(nodeInfo[i], true, true);
25424             }
25425             return;
25426         } 
25427         var node = this.getNode(nodeInfo);
25428         if(!node || this.isSelected(node)){
25429             return; // already selected.
25430         }
25431         if(!keepExisting){
25432             this.clearSelections(true);
25433         }
25434         
25435         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25436             Roo.fly(node).addClass(this.selectedClass);
25437             this.selections.push(node);
25438             if(!suppressEvent){
25439                 this.fireEvent("selectionchange", this, this.selections);
25440             }
25441         }
25442         
25443         
25444     },
25445       /**
25446      * Unselects nodes.
25447      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
25448      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25449      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25450      */
25451     unselect : function(nodeInfo, keepExisting, suppressEvent)
25452     {
25453         if(nodeInfo instanceof Array){
25454             Roo.each(this.selections, function(s) {
25455                 this.unselect(s, nodeInfo);
25456             }, this);
25457             return;
25458         }
25459         var node = this.getNode(nodeInfo);
25460         if(!node || !this.isSelected(node)){
25461             //Roo.log("not selected");
25462             return; // not selected.
25463         }
25464         // fireevent???
25465         var ns = [];
25466         Roo.each(this.selections, function(s) {
25467             if (s == node ) {
25468                 Roo.fly(node).removeClass(this.selectedClass);
25469
25470                 return;
25471             }
25472             ns.push(s);
25473         },this);
25474         
25475         this.selections= ns;
25476         this.fireEvent("selectionchange", this, this.selections);
25477     },
25478
25479     /**
25480      * Gets a template node.
25481      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25482      * @return {HTMLElement} The node or null if it wasn't found
25483      */
25484     getNode : function(nodeInfo){
25485         if(typeof nodeInfo == "string"){
25486             return document.getElementById(nodeInfo);
25487         }else if(typeof nodeInfo == "number"){
25488             return this.nodes[nodeInfo];
25489         }
25490         return nodeInfo;
25491     },
25492
25493     /**
25494      * Gets a range template nodes.
25495      * @param {Number} startIndex
25496      * @param {Number} endIndex
25497      * @return {Array} An array of nodes
25498      */
25499     getNodes : function(start, end){
25500         var ns = this.nodes;
25501         start = start || 0;
25502         end = typeof end == "undefined" ? ns.length - 1 : end;
25503         var nodes = [];
25504         if(start <= end){
25505             for(var i = start; i <= end; i++){
25506                 nodes.push(ns[i]);
25507             }
25508         } else{
25509             for(var i = start; i >= end; i--){
25510                 nodes.push(ns[i]);
25511             }
25512         }
25513         return nodes;
25514     },
25515
25516     /**
25517      * Finds the index of the passed node
25518      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25519      * @return {Number} The index of the node or -1
25520      */
25521     indexOf : function(node){
25522         node = this.getNode(node);
25523         if(typeof node.nodeIndex == "number"){
25524             return node.nodeIndex;
25525         }
25526         var ns = this.nodes;
25527         for(var i = 0, len = ns.length; i < len; i++){
25528             if(ns[i] == node){
25529                 return i;
25530             }
25531         }
25532         return -1;
25533     }
25534 });
25535 /*
25536  * Based on:
25537  * Ext JS Library 1.1.1
25538  * Copyright(c) 2006-2007, Ext JS, LLC.
25539  *
25540  * Originally Released Under LGPL - original licence link has changed is not relivant.
25541  *
25542  * Fork - LGPL
25543  * <script type="text/javascript">
25544  */
25545
25546 /**
25547  * @class Roo.JsonView
25548  * @extends Roo.View
25549  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25550 <pre><code>
25551 var view = new Roo.JsonView({
25552     container: "my-element",
25553     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25554     multiSelect: true, 
25555     jsonRoot: "data" 
25556 });
25557
25558 // listen for node click?
25559 view.on("click", function(vw, index, node, e){
25560     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25561 });
25562
25563 // direct load of JSON data
25564 view.load("foobar.php");
25565
25566 // Example from my blog list
25567 var tpl = new Roo.Template(
25568     '&lt;div class="entry"&gt;' +
25569     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25570     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25571     "&lt;/div&gt;&lt;hr /&gt;"
25572 );
25573
25574 var moreView = new Roo.JsonView({
25575     container :  "entry-list", 
25576     template : tpl,
25577     jsonRoot: "posts"
25578 });
25579 moreView.on("beforerender", this.sortEntries, this);
25580 moreView.load({
25581     url: "/blog/get-posts.php",
25582     params: "allposts=true",
25583     text: "Loading Blog Entries..."
25584 });
25585 </code></pre>
25586
25587 * Note: old code is supported with arguments : (container, template, config)
25588
25589
25590  * @constructor
25591  * Create a new JsonView
25592  * 
25593  * @param {Object} config The config object
25594  * 
25595  */
25596 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25597     
25598     
25599     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25600
25601     var um = this.el.getUpdateManager();
25602     um.setRenderer(this);
25603     um.on("update", this.onLoad, this);
25604     um.on("failure", this.onLoadException, this);
25605
25606     /**
25607      * @event beforerender
25608      * Fires before rendering of the downloaded JSON data.
25609      * @param {Roo.JsonView} this
25610      * @param {Object} data The JSON data loaded
25611      */
25612     /**
25613      * @event load
25614      * Fires when data is loaded.
25615      * @param {Roo.JsonView} this
25616      * @param {Object} data The JSON data loaded
25617      * @param {Object} response The raw Connect response object
25618      */
25619     /**
25620      * @event loadexception
25621      * Fires when loading fails.
25622      * @param {Roo.JsonView} this
25623      * @param {Object} response The raw Connect response object
25624      */
25625     this.addEvents({
25626         'beforerender' : true,
25627         'load' : true,
25628         'loadexception' : true
25629     });
25630 };
25631 Roo.extend(Roo.JsonView, Roo.View, {
25632     /**
25633      * @type {String} The root property in the loaded JSON object that contains the data
25634      */
25635     jsonRoot : "",
25636
25637     /**
25638      * Refreshes the view.
25639      */
25640     refresh : function(){
25641         this.clearSelections();
25642         this.el.update("");
25643         var html = [];
25644         var o = this.jsonData;
25645         if(o && o.length > 0){
25646             for(var i = 0, len = o.length; i < len; i++){
25647                 var data = this.prepareData(o[i], i, o);
25648                 html[html.length] = this.tpl.apply(data);
25649             }
25650         }else{
25651             html.push(this.emptyText);
25652         }
25653         this.el.update(html.join(""));
25654         this.nodes = this.el.dom.childNodes;
25655         this.updateIndexes(0);
25656     },
25657
25658     /**
25659      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
25660      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
25661      <pre><code>
25662      view.load({
25663          url: "your-url.php",
25664          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25665          callback: yourFunction,
25666          scope: yourObject, //(optional scope)
25667          discardUrl: false,
25668          nocache: false,
25669          text: "Loading...",
25670          timeout: 30,
25671          scripts: false
25672      });
25673      </code></pre>
25674      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25675      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
25676      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
25677      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25678      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
25679      */
25680     load : function(){
25681         var um = this.el.getUpdateManager();
25682         um.update.apply(um, arguments);
25683     },
25684
25685     render : function(el, response){
25686         this.clearSelections();
25687         this.el.update("");
25688         var o;
25689         try{
25690             o = Roo.util.JSON.decode(response.responseText);
25691             if(this.jsonRoot){
25692                 
25693                 o = o[this.jsonRoot];
25694             }
25695         } catch(e){
25696         }
25697         /**
25698          * The current JSON data or null
25699          */
25700         this.jsonData = o;
25701         this.beforeRender();
25702         this.refresh();
25703     },
25704
25705 /**
25706  * Get the number of records in the current JSON dataset
25707  * @return {Number}
25708  */
25709     getCount : function(){
25710         return this.jsonData ? this.jsonData.length : 0;
25711     },
25712
25713 /**
25714  * Returns the JSON object for the specified node(s)
25715  * @param {HTMLElement/Array} node The node or an array of nodes
25716  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25717  * you get the JSON object for the node
25718  */
25719     getNodeData : function(node){
25720         if(node instanceof Array){
25721             var data = [];
25722             for(var i = 0, len = node.length; i < len; i++){
25723                 data.push(this.getNodeData(node[i]));
25724             }
25725             return data;
25726         }
25727         return this.jsonData[this.indexOf(node)] || null;
25728     },
25729
25730     beforeRender : function(){
25731         this.snapshot = this.jsonData;
25732         if(this.sortInfo){
25733             this.sort.apply(this, this.sortInfo);
25734         }
25735         this.fireEvent("beforerender", this, this.jsonData);
25736     },
25737
25738     onLoad : function(el, o){
25739         this.fireEvent("load", this, this.jsonData, o);
25740     },
25741
25742     onLoadException : function(el, o){
25743         this.fireEvent("loadexception", this, o);
25744     },
25745
25746 /**
25747  * Filter the data by a specific property.
25748  * @param {String} property A property on your JSON objects
25749  * @param {String/RegExp} value Either string that the property values
25750  * should start with, or a RegExp to test against the property
25751  */
25752     filter : function(property, value){
25753         if(this.jsonData){
25754             var data = [];
25755             var ss = this.snapshot;
25756             if(typeof value == "string"){
25757                 var vlen = value.length;
25758                 if(vlen == 0){
25759                     this.clearFilter();
25760                     return;
25761                 }
25762                 value = value.toLowerCase();
25763                 for(var i = 0, len = ss.length; i < len; i++){
25764                     var o = ss[i];
25765                     if(o[property].substr(0, vlen).toLowerCase() == value){
25766                         data.push(o);
25767                     }
25768                 }
25769             } else if(value.exec){ // regex?
25770                 for(var i = 0, len = ss.length; i < len; i++){
25771                     var o = ss[i];
25772                     if(value.test(o[property])){
25773                         data.push(o);
25774                     }
25775                 }
25776             } else{
25777                 return;
25778             }
25779             this.jsonData = data;
25780             this.refresh();
25781         }
25782     },
25783
25784 /**
25785  * Filter by a function. The passed function will be called with each
25786  * object in the current dataset. If the function returns true the value is kept,
25787  * otherwise it is filtered.
25788  * @param {Function} fn
25789  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25790  */
25791     filterBy : function(fn, scope){
25792         if(this.jsonData){
25793             var data = [];
25794             var ss = this.snapshot;
25795             for(var i = 0, len = ss.length; i < len; i++){
25796                 var o = ss[i];
25797                 if(fn.call(scope || this, o)){
25798                     data.push(o);
25799                 }
25800             }
25801             this.jsonData = data;
25802             this.refresh();
25803         }
25804     },
25805
25806 /**
25807  * Clears the current filter.
25808  */
25809     clearFilter : function(){
25810         if(this.snapshot && this.jsonData != this.snapshot){
25811             this.jsonData = this.snapshot;
25812             this.refresh();
25813         }
25814     },
25815
25816
25817 /**
25818  * Sorts the data for this view and refreshes it.
25819  * @param {String} property A property on your JSON objects to sort on
25820  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25821  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25822  */
25823     sort : function(property, dir, sortType){
25824         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25825         if(this.jsonData){
25826             var p = property;
25827             var dsc = dir && dir.toLowerCase() == "desc";
25828             var f = function(o1, o2){
25829                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25830                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25831                 ;
25832                 if(v1 < v2){
25833                     return dsc ? +1 : -1;
25834                 } else if(v1 > v2){
25835                     return dsc ? -1 : +1;
25836                 } else{
25837                     return 0;
25838                 }
25839             };
25840             this.jsonData.sort(f);
25841             this.refresh();
25842             if(this.jsonData != this.snapshot){
25843                 this.snapshot.sort(f);
25844             }
25845         }
25846     }
25847 });/*
25848  * Based on:
25849  * Ext JS Library 1.1.1
25850  * Copyright(c) 2006-2007, Ext JS, LLC.
25851  *
25852  * Originally Released Under LGPL - original licence link has changed is not relivant.
25853  *
25854  * Fork - LGPL
25855  * <script type="text/javascript">
25856  */
25857  
25858
25859 /**
25860  * @class Roo.ColorPalette
25861  * @extends Roo.Component
25862  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25863  * Here's an example of typical usage:
25864  * <pre><code>
25865 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25866 cp.render('my-div');
25867
25868 cp.on('select', function(palette, selColor){
25869     // do something with selColor
25870 });
25871 </code></pre>
25872  * @constructor
25873  * Create a new ColorPalette
25874  * @param {Object} config The config object
25875  */
25876 Roo.ColorPalette = function(config){
25877     Roo.ColorPalette.superclass.constructor.call(this, config);
25878     this.addEvents({
25879         /**
25880              * @event select
25881              * Fires when a color is selected
25882              * @param {ColorPalette} this
25883              * @param {String} color The 6-digit color hex code (without the # symbol)
25884              */
25885         select: true
25886     });
25887
25888     if(this.handler){
25889         this.on("select", this.handler, this.scope, true);
25890     }
25891 };
25892 Roo.extend(Roo.ColorPalette, Roo.Component, {
25893     /**
25894      * @cfg {String} itemCls
25895      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25896      */
25897     itemCls : "x-color-palette",
25898     /**
25899      * @cfg {String} value
25900      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25901      * the hex codes are case-sensitive.
25902      */
25903     value : null,
25904     clickEvent:'click',
25905     // private
25906     ctype: "Roo.ColorPalette",
25907
25908     /**
25909      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25910      */
25911     allowReselect : false,
25912
25913     /**
25914      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25915      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25916      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25917      * of colors with the width setting until the box is symmetrical.</p>
25918      * <p>You can override individual colors if needed:</p>
25919      * <pre><code>
25920 var cp = new Roo.ColorPalette();
25921 cp.colors[0] = "FF0000";  // change the first box to red
25922 </code></pre>
25923
25924 Or you can provide a custom array of your own for complete control:
25925 <pre><code>
25926 var cp = new Roo.ColorPalette();
25927 cp.colors = ["000000", "993300", "333300"];
25928 </code></pre>
25929      * @type Array
25930      */
25931     colors : [
25932         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25933         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25934         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25935         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25936         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25937     ],
25938
25939     // private
25940     onRender : function(container, position){
25941         var t = new Roo.MasterTemplate(
25942             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25943         );
25944         var c = this.colors;
25945         for(var i = 0, len = c.length; i < len; i++){
25946             t.add([c[i]]);
25947         }
25948         var el = document.createElement("div");
25949         el.className = this.itemCls;
25950         t.overwrite(el);
25951         container.dom.insertBefore(el, position);
25952         this.el = Roo.get(el);
25953         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25954         if(this.clickEvent != 'click'){
25955             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25956         }
25957     },
25958
25959     // private
25960     afterRender : function(){
25961         Roo.ColorPalette.superclass.afterRender.call(this);
25962         if(this.value){
25963             var s = this.value;
25964             this.value = null;
25965             this.select(s);
25966         }
25967     },
25968
25969     // private
25970     handleClick : function(e, t){
25971         e.preventDefault();
25972         if(!this.disabled){
25973             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25974             this.select(c.toUpperCase());
25975         }
25976     },
25977
25978     /**
25979      * Selects the specified color in the palette (fires the select event)
25980      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25981      */
25982     select : function(color){
25983         color = color.replace("#", "");
25984         if(color != this.value || this.allowReselect){
25985             var el = this.el;
25986             if(this.value){
25987                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25988             }
25989             el.child("a.color-"+color).addClass("x-color-palette-sel");
25990             this.value = color;
25991             this.fireEvent("select", this, color);
25992         }
25993     }
25994 });/*
25995  * Based on:
25996  * Ext JS Library 1.1.1
25997  * Copyright(c) 2006-2007, Ext JS, LLC.
25998  *
25999  * Originally Released Under LGPL - original licence link has changed is not relivant.
26000  *
26001  * Fork - LGPL
26002  * <script type="text/javascript">
26003  */
26004  
26005 /**
26006  * @class Roo.DatePicker
26007  * @extends Roo.Component
26008  * Simple date picker class.
26009  * @constructor
26010  * Create a new DatePicker
26011  * @param {Object} config The config object
26012  */
26013 Roo.DatePicker = function(config){
26014     Roo.DatePicker.superclass.constructor.call(this, config);
26015
26016     this.value = config && config.value ?
26017                  config.value.clearTime() : new Date().clearTime();
26018
26019     this.addEvents({
26020         /**
26021              * @event select
26022              * Fires when a date is selected
26023              * @param {DatePicker} this
26024              * @param {Date} date The selected date
26025              */
26026         'select': true,
26027         /**
26028              * @event monthchange
26029              * Fires when the displayed month changes 
26030              * @param {DatePicker} this
26031              * @param {Date} date The selected month
26032              */
26033         'monthchange': true
26034     });
26035
26036     if(this.handler){
26037         this.on("select", this.handler,  this.scope || this);
26038     }
26039     // build the disabledDatesRE
26040     if(!this.disabledDatesRE && this.disabledDates){
26041         var dd = this.disabledDates;
26042         var re = "(?:";
26043         for(var i = 0; i < dd.length; i++){
26044             re += dd[i];
26045             if(i != dd.length-1) re += "|";
26046         }
26047         this.disabledDatesRE = new RegExp(re + ")");
26048     }
26049 };
26050
26051 Roo.extend(Roo.DatePicker, Roo.Component, {
26052     /**
26053      * @cfg {String} todayText
26054      * The text to display on the button that selects the current date (defaults to "Today")
26055      */
26056     todayText : "Today",
26057     /**
26058      * @cfg {String} okText
26059      * The text to display on the ok button
26060      */
26061     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26062     /**
26063      * @cfg {String} cancelText
26064      * The text to display on the cancel button
26065      */
26066     cancelText : "Cancel",
26067     /**
26068      * @cfg {String} todayTip
26069      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26070      */
26071     todayTip : "{0} (Spacebar)",
26072     /**
26073      * @cfg {Date} minDate
26074      * Minimum allowable date (JavaScript date object, defaults to null)
26075      */
26076     minDate : null,
26077     /**
26078      * @cfg {Date} maxDate
26079      * Maximum allowable date (JavaScript date object, defaults to null)
26080      */
26081     maxDate : null,
26082     /**
26083      * @cfg {String} minText
26084      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26085      */
26086     minText : "This date is before the minimum date",
26087     /**
26088      * @cfg {String} maxText
26089      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26090      */
26091     maxText : "This date is after the maximum date",
26092     /**
26093      * @cfg {String} format
26094      * The default date format string which can be overriden for localization support.  The format must be
26095      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26096      */
26097     format : "m/d/y",
26098     /**
26099      * @cfg {Array} disabledDays
26100      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26101      */
26102     disabledDays : null,
26103     /**
26104      * @cfg {String} disabledDaysText
26105      * The tooltip to display when the date falls on a disabled day (defaults to "")
26106      */
26107     disabledDaysText : "",
26108     /**
26109      * @cfg {RegExp} disabledDatesRE
26110      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26111      */
26112     disabledDatesRE : null,
26113     /**
26114      * @cfg {String} disabledDatesText
26115      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26116      */
26117     disabledDatesText : "",
26118     /**
26119      * @cfg {Boolean} constrainToViewport
26120      * True to constrain the date picker to the viewport (defaults to true)
26121      */
26122     constrainToViewport : true,
26123     /**
26124      * @cfg {Array} monthNames
26125      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26126      */
26127     monthNames : Date.monthNames,
26128     /**
26129      * @cfg {Array} dayNames
26130      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26131      */
26132     dayNames : Date.dayNames,
26133     /**
26134      * @cfg {String} nextText
26135      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26136      */
26137     nextText: 'Next Month (Control+Right)',
26138     /**
26139      * @cfg {String} prevText
26140      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26141      */
26142     prevText: 'Previous Month (Control+Left)',
26143     /**
26144      * @cfg {String} monthYearText
26145      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26146      */
26147     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26148     /**
26149      * @cfg {Number} startDay
26150      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26151      */
26152     startDay : 0,
26153     /**
26154      * @cfg {Bool} showClear
26155      * Show a clear button (usefull for date form elements that can be blank.)
26156      */
26157     
26158     showClear: false,
26159     
26160     /**
26161      * Sets the value of the date field
26162      * @param {Date} value The date to set
26163      */
26164     setValue : function(value){
26165         var old = this.value;
26166         
26167         if (typeof(value) == 'string') {
26168          
26169             value = Date.parseDate(value, this.format);
26170         }
26171         if (!value) {
26172             value = new Date();
26173         }
26174         
26175         this.value = value.clearTime(true);
26176         if(this.el){
26177             this.update(this.value);
26178         }
26179     },
26180
26181     /**
26182      * Gets the current selected value of the date field
26183      * @return {Date} The selected date
26184      */
26185     getValue : function(){
26186         return this.value;
26187     },
26188
26189     // private
26190     focus : function(){
26191         if(this.el){
26192             this.update(this.activeDate);
26193         }
26194     },
26195
26196     // privateval
26197     onRender : function(container, position){
26198         
26199         var m = [
26200              '<table cellspacing="0">',
26201                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
26202                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26203         var dn = this.dayNames;
26204         for(var i = 0; i < 7; i++){
26205             var d = this.startDay+i;
26206             if(d > 6){
26207                 d = d-7;
26208             }
26209             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26210         }
26211         m[m.length] = "</tr></thead><tbody><tr>";
26212         for(var i = 0; i < 42; i++) {
26213             if(i % 7 == 0 && i != 0){
26214                 m[m.length] = "</tr><tr>";
26215             }
26216             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26217         }
26218         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26219             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26220
26221         var el = document.createElement("div");
26222         el.className = "x-date-picker";
26223         el.innerHTML = m.join("");
26224
26225         container.dom.insertBefore(el, position);
26226
26227         this.el = Roo.get(el);
26228         this.eventEl = Roo.get(el.firstChild);
26229
26230         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26231             handler: this.showPrevMonth,
26232             scope: this,
26233             preventDefault:true,
26234             stopDefault:true
26235         });
26236
26237         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26238             handler: this.showNextMonth,
26239             scope: this,
26240             preventDefault:true,
26241             stopDefault:true
26242         });
26243
26244         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26245
26246         this.monthPicker = this.el.down('div.x-date-mp');
26247         this.monthPicker.enableDisplayMode('block');
26248         
26249         var kn = new Roo.KeyNav(this.eventEl, {
26250             "left" : function(e){
26251                 e.ctrlKey ?
26252                     this.showPrevMonth() :
26253                     this.update(this.activeDate.add("d", -1));
26254             },
26255
26256             "right" : function(e){
26257                 e.ctrlKey ?
26258                     this.showNextMonth() :
26259                     this.update(this.activeDate.add("d", 1));
26260             },
26261
26262             "up" : function(e){
26263                 e.ctrlKey ?
26264                     this.showNextYear() :
26265                     this.update(this.activeDate.add("d", -7));
26266             },
26267
26268             "down" : function(e){
26269                 e.ctrlKey ?
26270                     this.showPrevYear() :
26271                     this.update(this.activeDate.add("d", 7));
26272             },
26273
26274             "pageUp" : function(e){
26275                 this.showNextMonth();
26276             },
26277
26278             "pageDown" : function(e){
26279                 this.showPrevMonth();
26280             },
26281
26282             "enter" : function(e){
26283                 e.stopPropagation();
26284                 return true;
26285             },
26286
26287             scope : this
26288         });
26289
26290         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26291
26292         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26293
26294         this.el.unselectable();
26295         
26296         this.cells = this.el.select("table.x-date-inner tbody td");
26297         this.textNodes = this.el.query("table.x-date-inner tbody span");
26298
26299         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26300             text: "&#160;",
26301             tooltip: this.monthYearText
26302         });
26303
26304         this.mbtn.on('click', this.showMonthPicker, this);
26305         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26306
26307
26308         var today = (new Date()).dateFormat(this.format);
26309         
26310         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26311         if (this.showClear) {
26312             baseTb.add( new Roo.Toolbar.Fill());
26313         }
26314         baseTb.add({
26315             text: String.format(this.todayText, today),
26316             tooltip: String.format(this.todayTip, today),
26317             handler: this.selectToday,
26318             scope: this
26319         });
26320         
26321         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26322             
26323         //});
26324         if (this.showClear) {
26325             
26326             baseTb.add( new Roo.Toolbar.Fill());
26327             baseTb.add({
26328                 text: '&#160;',
26329                 cls: 'x-btn-icon x-btn-clear',
26330                 handler: function() {
26331                     //this.value = '';
26332                     this.fireEvent("select", this, '');
26333                 },
26334                 scope: this
26335             });
26336         }
26337         
26338         
26339         if(Roo.isIE){
26340             this.el.repaint();
26341         }
26342         this.update(this.value);
26343     },
26344
26345     createMonthPicker : function(){
26346         if(!this.monthPicker.dom.firstChild){
26347             var buf = ['<table border="0" cellspacing="0">'];
26348             for(var i = 0; i < 6; i++){
26349                 buf.push(
26350                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26351                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26352                     i == 0 ?
26353                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
26354                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26355                 );
26356             }
26357             buf.push(
26358                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26359                     this.okText,
26360                     '</button><button type="button" class="x-date-mp-cancel">',
26361                     this.cancelText,
26362                     '</button></td></tr>',
26363                 '</table>'
26364             );
26365             this.monthPicker.update(buf.join(''));
26366             this.monthPicker.on('click', this.onMonthClick, this);
26367             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26368
26369             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26370             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26371
26372             this.mpMonths.each(function(m, a, i){
26373                 i += 1;
26374                 if((i%2) == 0){
26375                     m.dom.xmonth = 5 + Math.round(i * .5);
26376                 }else{
26377                     m.dom.xmonth = Math.round((i-1) * .5);
26378                 }
26379             });
26380         }
26381     },
26382
26383     showMonthPicker : function(){
26384         this.createMonthPicker();
26385         var size = this.el.getSize();
26386         this.monthPicker.setSize(size);
26387         this.monthPicker.child('table').setSize(size);
26388
26389         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26390         this.updateMPMonth(this.mpSelMonth);
26391         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26392         this.updateMPYear(this.mpSelYear);
26393
26394         this.monthPicker.slideIn('t', {duration:.2});
26395     },
26396
26397     updateMPYear : function(y){
26398         this.mpyear = y;
26399         var ys = this.mpYears.elements;
26400         for(var i = 1; i <= 10; i++){
26401             var td = ys[i-1], y2;
26402             if((i%2) == 0){
26403                 y2 = y + Math.round(i * .5);
26404                 td.firstChild.innerHTML = y2;
26405                 td.xyear = y2;
26406             }else{
26407                 y2 = y - (5-Math.round(i * .5));
26408                 td.firstChild.innerHTML = y2;
26409                 td.xyear = y2;
26410             }
26411             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26412         }
26413     },
26414
26415     updateMPMonth : function(sm){
26416         this.mpMonths.each(function(m, a, i){
26417             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26418         });
26419     },
26420
26421     selectMPMonth: function(m){
26422         
26423     },
26424
26425     onMonthClick : function(e, t){
26426         e.stopEvent();
26427         var el = new Roo.Element(t), pn;
26428         if(el.is('button.x-date-mp-cancel')){
26429             this.hideMonthPicker();
26430         }
26431         else if(el.is('button.x-date-mp-ok')){
26432             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26433             this.hideMonthPicker();
26434         }
26435         else if(pn = el.up('td.x-date-mp-month', 2)){
26436             this.mpMonths.removeClass('x-date-mp-sel');
26437             pn.addClass('x-date-mp-sel');
26438             this.mpSelMonth = pn.dom.xmonth;
26439         }
26440         else if(pn = el.up('td.x-date-mp-year', 2)){
26441             this.mpYears.removeClass('x-date-mp-sel');
26442             pn.addClass('x-date-mp-sel');
26443             this.mpSelYear = pn.dom.xyear;
26444         }
26445         else if(el.is('a.x-date-mp-prev')){
26446             this.updateMPYear(this.mpyear-10);
26447         }
26448         else if(el.is('a.x-date-mp-next')){
26449             this.updateMPYear(this.mpyear+10);
26450         }
26451     },
26452
26453     onMonthDblClick : function(e, t){
26454         e.stopEvent();
26455         var el = new Roo.Element(t), pn;
26456         if(pn = el.up('td.x-date-mp-month', 2)){
26457             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26458             this.hideMonthPicker();
26459         }
26460         else if(pn = el.up('td.x-date-mp-year', 2)){
26461             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26462             this.hideMonthPicker();
26463         }
26464     },
26465
26466     hideMonthPicker : function(disableAnim){
26467         if(this.monthPicker){
26468             if(disableAnim === true){
26469                 this.monthPicker.hide();
26470             }else{
26471                 this.monthPicker.slideOut('t', {duration:.2});
26472             }
26473         }
26474     },
26475
26476     // private
26477     showPrevMonth : function(e){
26478         this.update(this.activeDate.add("mo", -1));
26479     },
26480
26481     // private
26482     showNextMonth : function(e){
26483         this.update(this.activeDate.add("mo", 1));
26484     },
26485
26486     // private
26487     showPrevYear : function(){
26488         this.update(this.activeDate.add("y", -1));
26489     },
26490
26491     // private
26492     showNextYear : function(){
26493         this.update(this.activeDate.add("y", 1));
26494     },
26495
26496     // private
26497     handleMouseWheel : function(e){
26498         var delta = e.getWheelDelta();
26499         if(delta > 0){
26500             this.showPrevMonth();
26501             e.stopEvent();
26502         } else if(delta < 0){
26503             this.showNextMonth();
26504             e.stopEvent();
26505         }
26506     },
26507
26508     // private
26509     handleDateClick : function(e, t){
26510         e.stopEvent();
26511         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26512             this.setValue(new Date(t.dateValue));
26513             this.fireEvent("select", this, this.value);
26514         }
26515     },
26516
26517     // private
26518     selectToday : function(){
26519         this.setValue(new Date().clearTime());
26520         this.fireEvent("select", this, this.value);
26521     },
26522
26523     // private
26524     update : function(date)
26525     {
26526         var vd = this.activeDate;
26527         this.activeDate = date;
26528         if(vd && this.el){
26529             var t = date.getTime();
26530             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26531                 this.cells.removeClass("x-date-selected");
26532                 this.cells.each(function(c){
26533                    if(c.dom.firstChild.dateValue == t){
26534                        c.addClass("x-date-selected");
26535                        setTimeout(function(){
26536                             try{c.dom.firstChild.focus();}catch(e){}
26537                        }, 50);
26538                        return false;
26539                    }
26540                 });
26541                 return;
26542             }
26543         }
26544         
26545         var days = date.getDaysInMonth();
26546         var firstOfMonth = date.getFirstDateOfMonth();
26547         var startingPos = firstOfMonth.getDay()-this.startDay;
26548
26549         if(startingPos <= this.startDay){
26550             startingPos += 7;
26551         }
26552
26553         var pm = date.add("mo", -1);
26554         var prevStart = pm.getDaysInMonth()-startingPos;
26555
26556         var cells = this.cells.elements;
26557         var textEls = this.textNodes;
26558         days += startingPos;
26559
26560         // convert everything to numbers so it's fast
26561         var day = 86400000;
26562         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26563         var today = new Date().clearTime().getTime();
26564         var sel = date.clearTime().getTime();
26565         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26566         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26567         var ddMatch = this.disabledDatesRE;
26568         var ddText = this.disabledDatesText;
26569         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26570         var ddaysText = this.disabledDaysText;
26571         var format = this.format;
26572
26573         var setCellClass = function(cal, cell){
26574             cell.title = "";
26575             var t = d.getTime();
26576             cell.firstChild.dateValue = t;
26577             if(t == today){
26578                 cell.className += " x-date-today";
26579                 cell.title = cal.todayText;
26580             }
26581             if(t == sel){
26582                 cell.className += " x-date-selected";
26583                 setTimeout(function(){
26584                     try{cell.firstChild.focus();}catch(e){}
26585                 }, 50);
26586             }
26587             // disabling
26588             if(t < min) {
26589                 cell.className = " x-date-disabled";
26590                 cell.title = cal.minText;
26591                 return;
26592             }
26593             if(t > max) {
26594                 cell.className = " x-date-disabled";
26595                 cell.title = cal.maxText;
26596                 return;
26597             }
26598             if(ddays){
26599                 if(ddays.indexOf(d.getDay()) != -1){
26600                     cell.title = ddaysText;
26601                     cell.className = " x-date-disabled";
26602                 }
26603             }
26604             if(ddMatch && format){
26605                 var fvalue = d.dateFormat(format);
26606                 if(ddMatch.test(fvalue)){
26607                     cell.title = ddText.replace("%0", fvalue);
26608                     cell.className = " x-date-disabled";
26609                 }
26610             }
26611         };
26612
26613         var i = 0;
26614         for(; i < startingPos; i++) {
26615             textEls[i].innerHTML = (++prevStart);
26616             d.setDate(d.getDate()+1);
26617             cells[i].className = "x-date-prevday";
26618             setCellClass(this, cells[i]);
26619         }
26620         for(; i < days; i++){
26621             intDay = i - startingPos + 1;
26622             textEls[i].innerHTML = (intDay);
26623             d.setDate(d.getDate()+1);
26624             cells[i].className = "x-date-active";
26625             setCellClass(this, cells[i]);
26626         }
26627         var extraDays = 0;
26628         for(; i < 42; i++) {
26629              textEls[i].innerHTML = (++extraDays);
26630              d.setDate(d.getDate()+1);
26631              cells[i].className = "x-date-nextday";
26632              setCellClass(this, cells[i]);
26633         }
26634
26635         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26636         this.fireEvent('monthchange', this, date);
26637         
26638         if(!this.internalRender){
26639             var main = this.el.dom.firstChild;
26640             var w = main.offsetWidth;
26641             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26642             Roo.fly(main).setWidth(w);
26643             this.internalRender = true;
26644             // opera does not respect the auto grow header center column
26645             // then, after it gets a width opera refuses to recalculate
26646             // without a second pass
26647             if(Roo.isOpera && !this.secondPass){
26648                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26649                 this.secondPass = true;
26650                 this.update.defer(10, this, [date]);
26651             }
26652         }
26653         
26654         
26655     }
26656 });        /*
26657  * Based on:
26658  * Ext JS Library 1.1.1
26659  * Copyright(c) 2006-2007, Ext JS, LLC.
26660  *
26661  * Originally Released Under LGPL - original licence link has changed is not relivant.
26662  *
26663  * Fork - LGPL
26664  * <script type="text/javascript">
26665  */
26666 /**
26667  * @class Roo.TabPanel
26668  * @extends Roo.util.Observable
26669  * A lightweight tab container.
26670  * <br><br>
26671  * Usage:
26672  * <pre><code>
26673 // basic tabs 1, built from existing content
26674 var tabs = new Roo.TabPanel("tabs1");
26675 tabs.addTab("script", "View Script");
26676 tabs.addTab("markup", "View Markup");
26677 tabs.activate("script");
26678
26679 // more advanced tabs, built from javascript
26680 var jtabs = new Roo.TabPanel("jtabs");
26681 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26682
26683 // set up the UpdateManager
26684 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26685 var updater = tab2.getUpdateManager();
26686 updater.setDefaultUrl("ajax1.htm");
26687 tab2.on('activate', updater.refresh, updater, true);
26688
26689 // Use setUrl for Ajax loading
26690 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26691 tab3.setUrl("ajax2.htm", null, true);
26692
26693 // Disabled tab
26694 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26695 tab4.disable();
26696
26697 jtabs.activate("jtabs-1");
26698  * </code></pre>
26699  * @constructor
26700  * Create a new TabPanel.
26701  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26702  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26703  */
26704 Roo.TabPanel = function(container, config){
26705     /**
26706     * The container element for this TabPanel.
26707     * @type Roo.Element
26708     */
26709     this.el = Roo.get(container, true);
26710     if(config){
26711         if(typeof config == "boolean"){
26712             this.tabPosition = config ? "bottom" : "top";
26713         }else{
26714             Roo.apply(this, config);
26715         }
26716     }
26717     if(this.tabPosition == "bottom"){
26718         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26719         this.el.addClass("x-tabs-bottom");
26720     }
26721     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26722     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26723     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26724     if(Roo.isIE){
26725         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26726     }
26727     if(this.tabPosition != "bottom"){
26728         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26729          * @type Roo.Element
26730          */
26731         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26732         this.el.addClass("x-tabs-top");
26733     }
26734     this.items = [];
26735
26736     this.bodyEl.setStyle("position", "relative");
26737
26738     this.active = null;
26739     this.activateDelegate = this.activate.createDelegate(this);
26740
26741     this.addEvents({
26742         /**
26743          * @event tabchange
26744          * Fires when the active tab changes
26745          * @param {Roo.TabPanel} this
26746          * @param {Roo.TabPanelItem} activePanel The new active tab
26747          */
26748         "tabchange": true,
26749         /**
26750          * @event beforetabchange
26751          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26752          * @param {Roo.TabPanel} this
26753          * @param {Object} e Set cancel to true on this object to cancel the tab change
26754          * @param {Roo.TabPanelItem} tab The tab being changed to
26755          */
26756         "beforetabchange" : true
26757     });
26758
26759     Roo.EventManager.onWindowResize(this.onResize, this);
26760     this.cpad = this.el.getPadding("lr");
26761     this.hiddenCount = 0;
26762
26763
26764     // toolbar on the tabbar support...
26765     if (this.toolbar) {
26766         var tcfg = this.toolbar;
26767         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26768         this.toolbar = new Roo.Toolbar(tcfg);
26769         if (Roo.isSafari) {
26770             var tbl = tcfg.container.child('table', true);
26771             tbl.setAttribute('width', '100%');
26772         }
26773         
26774     }
26775    
26776
26777
26778     Roo.TabPanel.superclass.constructor.call(this);
26779 };
26780
26781 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26782     /*
26783      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26784      */
26785     tabPosition : "top",
26786     /*
26787      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26788      */
26789     currentTabWidth : 0,
26790     /*
26791      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26792      */
26793     minTabWidth : 40,
26794     /*
26795      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26796      */
26797     maxTabWidth : 250,
26798     /*
26799      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26800      */
26801     preferredTabWidth : 175,
26802     /*
26803      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26804      */
26805     resizeTabs : false,
26806     /*
26807      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26808      */
26809     monitorResize : true,
26810     /*
26811      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26812      */
26813     toolbar : false,
26814
26815     /**
26816      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26817      * @param {String} id The id of the div to use <b>or create</b>
26818      * @param {String} text The text for the tab
26819      * @param {String} content (optional) Content to put in the TabPanelItem body
26820      * @param {Boolean} closable (optional) True to create a close icon on the tab
26821      * @return {Roo.TabPanelItem} The created TabPanelItem
26822      */
26823     addTab : function(id, text, content, closable){
26824         var item = new Roo.TabPanelItem(this, id, text, closable);
26825         this.addTabItem(item);
26826         if(content){
26827             item.setContent(content);
26828         }
26829         return item;
26830     },
26831
26832     /**
26833      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26834      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26835      * @return {Roo.TabPanelItem}
26836      */
26837     getTab : function(id){
26838         return this.items[id];
26839     },
26840
26841     /**
26842      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26843      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26844      */
26845     hideTab : function(id){
26846         var t = this.items[id];
26847         if(!t.isHidden()){
26848            t.setHidden(true);
26849            this.hiddenCount++;
26850            this.autoSizeTabs();
26851         }
26852     },
26853
26854     /**
26855      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26856      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26857      */
26858     unhideTab : function(id){
26859         var t = this.items[id];
26860         if(t.isHidden()){
26861            t.setHidden(false);
26862            this.hiddenCount--;
26863            this.autoSizeTabs();
26864         }
26865     },
26866
26867     /**
26868      * Adds an existing {@link Roo.TabPanelItem}.
26869      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26870      */
26871     addTabItem : function(item){
26872         this.items[item.id] = item;
26873         this.items.push(item);
26874         if(this.resizeTabs){
26875            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26876            this.autoSizeTabs();
26877         }else{
26878             item.autoSize();
26879         }
26880     },
26881
26882     /**
26883      * Removes a {@link Roo.TabPanelItem}.
26884      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26885      */
26886     removeTab : function(id){
26887         var items = this.items;
26888         var tab = items[id];
26889         if(!tab) { return; }
26890         var index = items.indexOf(tab);
26891         if(this.active == tab && items.length > 1){
26892             var newTab = this.getNextAvailable(index);
26893             if(newTab) {
26894                 newTab.activate();
26895             }
26896         }
26897         this.stripEl.dom.removeChild(tab.pnode.dom);
26898         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26899             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26900         }
26901         items.splice(index, 1);
26902         delete this.items[tab.id];
26903         tab.fireEvent("close", tab);
26904         tab.purgeListeners();
26905         this.autoSizeTabs();
26906     },
26907
26908     getNextAvailable : function(start){
26909         var items = this.items;
26910         var index = start;
26911         // look for a next tab that will slide over to
26912         // replace the one being removed
26913         while(index < items.length){
26914             var item = items[++index];
26915             if(item && !item.isHidden()){
26916                 return item;
26917             }
26918         }
26919         // if one isn't found select the previous tab (on the left)
26920         index = start;
26921         while(index >= 0){
26922             var item = items[--index];
26923             if(item && !item.isHidden()){
26924                 return item;
26925             }
26926         }
26927         return null;
26928     },
26929
26930     /**
26931      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26932      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26933      */
26934     disableTab : function(id){
26935         var tab = this.items[id];
26936         if(tab && this.active != tab){
26937             tab.disable();
26938         }
26939     },
26940
26941     /**
26942      * Enables a {@link Roo.TabPanelItem} that is disabled.
26943      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26944      */
26945     enableTab : function(id){
26946         var tab = this.items[id];
26947         tab.enable();
26948     },
26949
26950     /**
26951      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26952      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26953      * @return {Roo.TabPanelItem} The TabPanelItem.
26954      */
26955     activate : function(id){
26956         var tab = this.items[id];
26957         if(!tab){
26958             return null;
26959         }
26960         if(tab == this.active || tab.disabled){
26961             return tab;
26962         }
26963         var e = {};
26964         this.fireEvent("beforetabchange", this, e, tab);
26965         if(e.cancel !== true && !tab.disabled){
26966             if(this.active){
26967                 this.active.hide();
26968             }
26969             this.active = this.items[id];
26970             this.active.show();
26971             this.fireEvent("tabchange", this, this.active);
26972         }
26973         return tab;
26974     },
26975
26976     /**
26977      * Gets the active {@link Roo.TabPanelItem}.
26978      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26979      */
26980     getActiveTab : function(){
26981         return this.active;
26982     },
26983
26984     /**
26985      * Updates the tab body element to fit the height of the container element
26986      * for overflow scrolling
26987      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26988      */
26989     syncHeight : function(targetHeight){
26990         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26991         var bm = this.bodyEl.getMargins();
26992         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26993         this.bodyEl.setHeight(newHeight);
26994         return newHeight;
26995     },
26996
26997     onResize : function(){
26998         if(this.monitorResize){
26999             this.autoSizeTabs();
27000         }
27001     },
27002
27003     /**
27004      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
27005      */
27006     beginUpdate : function(){
27007         this.updating = true;
27008     },
27009
27010     /**
27011      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
27012      */
27013     endUpdate : function(){
27014         this.updating = false;
27015         this.autoSizeTabs();
27016     },
27017
27018     /**
27019      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27020      */
27021     autoSizeTabs : function(){
27022         var count = this.items.length;
27023         var vcount = count - this.hiddenCount;
27024         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
27025         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27026         var availWidth = Math.floor(w / vcount);
27027         var b = this.stripBody;
27028         if(b.getWidth() > w){
27029             var tabs = this.items;
27030             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27031             if(availWidth < this.minTabWidth){
27032                 /*if(!this.sleft){    // incomplete scrolling code
27033                     this.createScrollButtons();
27034                 }
27035                 this.showScroll();
27036                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27037             }
27038         }else{
27039             if(this.currentTabWidth < this.preferredTabWidth){
27040                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27041             }
27042         }
27043     },
27044
27045     /**
27046      * Returns the number of tabs in this TabPanel.
27047      * @return {Number}
27048      */
27049      getCount : function(){
27050          return this.items.length;
27051      },
27052
27053     /**
27054      * Resizes all the tabs to the passed width
27055      * @param {Number} The new width
27056      */
27057     setTabWidth : function(width){
27058         this.currentTabWidth = width;
27059         for(var i = 0, len = this.items.length; i < len; i++) {
27060                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27061         }
27062     },
27063
27064     /**
27065      * Destroys this TabPanel
27066      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27067      */
27068     destroy : function(removeEl){
27069         Roo.EventManager.removeResizeListener(this.onResize, this);
27070         for(var i = 0, len = this.items.length; i < len; i++){
27071             this.items[i].purgeListeners();
27072         }
27073         if(removeEl === true){
27074             this.el.update("");
27075             this.el.remove();
27076         }
27077     }
27078 });
27079
27080 /**
27081  * @class Roo.TabPanelItem
27082  * @extends Roo.util.Observable
27083  * Represents an individual item (tab plus body) in a TabPanel.
27084  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27085  * @param {String} id The id of this TabPanelItem
27086  * @param {String} text The text for the tab of this TabPanelItem
27087  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27088  */
27089 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27090     /**
27091      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27092      * @type Roo.TabPanel
27093      */
27094     this.tabPanel = tabPanel;
27095     /**
27096      * The id for this TabPanelItem
27097      * @type String
27098      */
27099     this.id = id;
27100     /** @private */
27101     this.disabled = false;
27102     /** @private */
27103     this.text = text;
27104     /** @private */
27105     this.loaded = false;
27106     this.closable = closable;
27107
27108     /**
27109      * The body element for this TabPanelItem.
27110      * @type Roo.Element
27111      */
27112     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27113     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27114     this.bodyEl.setStyle("display", "block");
27115     this.bodyEl.setStyle("zoom", "1");
27116     this.hideAction();
27117
27118     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27119     /** @private */
27120     this.el = Roo.get(els.el, true);
27121     this.inner = Roo.get(els.inner, true);
27122     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27123     this.pnode = Roo.get(els.el.parentNode, true);
27124     this.el.on("mousedown", this.onTabMouseDown, this);
27125     this.el.on("click", this.onTabClick, this);
27126     /** @private */
27127     if(closable){
27128         var c = Roo.get(els.close, true);
27129         c.dom.title = this.closeText;
27130         c.addClassOnOver("close-over");
27131         c.on("click", this.closeClick, this);
27132      }
27133
27134     this.addEvents({
27135          /**
27136          * @event activate
27137          * Fires when this tab becomes the active tab.
27138          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27139          * @param {Roo.TabPanelItem} this
27140          */
27141         "activate": true,
27142         /**
27143          * @event beforeclose
27144          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27145          * @param {Roo.TabPanelItem} this
27146          * @param {Object} e Set cancel to true on this object to cancel the close.
27147          */
27148         "beforeclose": true,
27149         /**
27150          * @event close
27151          * Fires when this tab is closed.
27152          * @param {Roo.TabPanelItem} this
27153          */
27154          "close": true,
27155         /**
27156          * @event deactivate
27157          * Fires when this tab is no longer the active tab.
27158          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27159          * @param {Roo.TabPanelItem} this
27160          */
27161          "deactivate" : true
27162     });
27163     this.hidden = false;
27164
27165     Roo.TabPanelItem.superclass.constructor.call(this);
27166 };
27167
27168 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27169     purgeListeners : function(){
27170        Roo.util.Observable.prototype.purgeListeners.call(this);
27171        this.el.removeAllListeners();
27172     },
27173     /**
27174      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27175      */
27176     show : function(){
27177         this.pnode.addClass("on");
27178         this.showAction();
27179         if(Roo.isOpera){
27180             this.tabPanel.stripWrap.repaint();
27181         }
27182         this.fireEvent("activate", this.tabPanel, this);
27183     },
27184
27185     /**
27186      * Returns true if this tab is the active tab.
27187      * @return {Boolean}
27188      */
27189     isActive : function(){
27190         return this.tabPanel.getActiveTab() == this;
27191     },
27192
27193     /**
27194      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27195      */
27196     hide : function(){
27197         this.pnode.removeClass("on");
27198         this.hideAction();
27199         this.fireEvent("deactivate", this.tabPanel, this);
27200     },
27201
27202     hideAction : function(){
27203         this.bodyEl.hide();
27204         this.bodyEl.setStyle("position", "absolute");
27205         this.bodyEl.setLeft("-20000px");
27206         this.bodyEl.setTop("-20000px");
27207     },
27208
27209     showAction : function(){
27210         this.bodyEl.setStyle("position", "relative");
27211         this.bodyEl.setTop("");
27212         this.bodyEl.setLeft("");
27213         this.bodyEl.show();
27214     },
27215
27216     /**
27217      * Set the tooltip for the tab.
27218      * @param {String} tooltip The tab's tooltip
27219      */
27220     setTooltip : function(text){
27221         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27222             this.textEl.dom.qtip = text;
27223             this.textEl.dom.removeAttribute('title');
27224         }else{
27225             this.textEl.dom.title = text;
27226         }
27227     },
27228
27229     onTabClick : function(e){
27230         e.preventDefault();
27231         this.tabPanel.activate(this.id);
27232     },
27233
27234     onTabMouseDown : function(e){
27235         e.preventDefault();
27236         this.tabPanel.activate(this.id);
27237     },
27238
27239     getWidth : function(){
27240         return this.inner.getWidth();
27241     },
27242
27243     setWidth : function(width){
27244         var iwidth = width - this.pnode.getPadding("lr");
27245         this.inner.setWidth(iwidth);
27246         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27247         this.pnode.setWidth(width);
27248     },
27249
27250     /**
27251      * Show or hide the tab
27252      * @param {Boolean} hidden True to hide or false to show.
27253      */
27254     setHidden : function(hidden){
27255         this.hidden = hidden;
27256         this.pnode.setStyle("display", hidden ? "none" : "");
27257     },
27258
27259     /**
27260      * Returns true if this tab is "hidden"
27261      * @return {Boolean}
27262      */
27263     isHidden : function(){
27264         return this.hidden;
27265     },
27266
27267     /**
27268      * Returns the text for this tab
27269      * @return {String}
27270      */
27271     getText : function(){
27272         return this.text;
27273     },
27274
27275     autoSize : function(){
27276         //this.el.beginMeasure();
27277         this.textEl.setWidth(1);
27278         /*
27279          *  #2804 [new] Tabs in Roojs
27280          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27281          */
27282         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27283         //this.el.endMeasure();
27284     },
27285
27286     /**
27287      * Sets the text for the tab (Note: this also sets the tooltip text)
27288      * @param {String} text The tab's text and tooltip
27289      */
27290     setText : function(text){
27291         this.text = text;
27292         this.textEl.update(text);
27293         this.setTooltip(text);
27294         if(!this.tabPanel.resizeTabs){
27295             this.autoSize();
27296         }
27297     },
27298     /**
27299      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27300      */
27301     activate : function(){
27302         this.tabPanel.activate(this.id);
27303     },
27304
27305     /**
27306      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27307      */
27308     disable : function(){
27309         if(this.tabPanel.active != this){
27310             this.disabled = true;
27311             this.pnode.addClass("disabled");
27312         }
27313     },
27314
27315     /**
27316      * Enables this TabPanelItem if it was previously disabled.
27317      */
27318     enable : function(){
27319         this.disabled = false;
27320         this.pnode.removeClass("disabled");
27321     },
27322
27323     /**
27324      * Sets the content for this TabPanelItem.
27325      * @param {String} content The content
27326      * @param {Boolean} loadScripts true to look for and load scripts
27327      */
27328     setContent : function(content, loadScripts){
27329         this.bodyEl.update(content, loadScripts);
27330     },
27331
27332     /**
27333      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27334      * @return {Roo.UpdateManager} The UpdateManager
27335      */
27336     getUpdateManager : function(){
27337         return this.bodyEl.getUpdateManager();
27338     },
27339
27340     /**
27341      * Set a URL to be used to load the content for this TabPanelItem.
27342      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27343      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
27344      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
27345      * @return {Roo.UpdateManager} The UpdateManager
27346      */
27347     setUrl : function(url, params, loadOnce){
27348         if(this.refreshDelegate){
27349             this.un('activate', this.refreshDelegate);
27350         }
27351         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27352         this.on("activate", this.refreshDelegate);
27353         return this.bodyEl.getUpdateManager();
27354     },
27355
27356     /** @private */
27357     _handleRefresh : function(url, params, loadOnce){
27358         if(!loadOnce || !this.loaded){
27359             var updater = this.bodyEl.getUpdateManager();
27360             updater.update(url, params, this._setLoaded.createDelegate(this));
27361         }
27362     },
27363
27364     /**
27365      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27366      *   Will fail silently if the setUrl method has not been called.
27367      *   This does not activate the panel, just updates its content.
27368      */
27369     refresh : function(){
27370         if(this.refreshDelegate){
27371            this.loaded = false;
27372            this.refreshDelegate();
27373         }
27374     },
27375
27376     /** @private */
27377     _setLoaded : function(){
27378         this.loaded = true;
27379     },
27380
27381     /** @private */
27382     closeClick : function(e){
27383         var o = {};
27384         e.stopEvent();
27385         this.fireEvent("beforeclose", this, o);
27386         if(o.cancel !== true){
27387             this.tabPanel.removeTab(this.id);
27388         }
27389     },
27390     /**
27391      * The text displayed in the tooltip for the close icon.
27392      * @type String
27393      */
27394     closeText : "Close this tab"
27395 });
27396
27397 /** @private */
27398 Roo.TabPanel.prototype.createStrip = function(container){
27399     var strip = document.createElement("div");
27400     strip.className = "x-tabs-wrap";
27401     container.appendChild(strip);
27402     return strip;
27403 };
27404 /** @private */
27405 Roo.TabPanel.prototype.createStripList = function(strip){
27406     // div wrapper for retard IE
27407     // returns the "tr" element.
27408     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27409         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27410         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27411     return strip.firstChild.firstChild.firstChild.firstChild;
27412 };
27413 /** @private */
27414 Roo.TabPanel.prototype.createBody = function(container){
27415     var body = document.createElement("div");
27416     Roo.id(body, "tab-body");
27417     Roo.fly(body).addClass("x-tabs-body");
27418     container.appendChild(body);
27419     return body;
27420 };
27421 /** @private */
27422 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27423     var body = Roo.getDom(id);
27424     if(!body){
27425         body = document.createElement("div");
27426         body.id = id;
27427     }
27428     Roo.fly(body).addClass("x-tabs-item-body");
27429     bodyEl.insertBefore(body, bodyEl.firstChild);
27430     return body;
27431 };
27432 /** @private */
27433 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27434     var td = document.createElement("td");
27435     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27436     //stripEl.appendChild(td);
27437     if(closable){
27438         td.className = "x-tabs-closable";
27439         if(!this.closeTpl){
27440             this.closeTpl = new Roo.Template(
27441                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27442                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27443                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27444             );
27445         }
27446         var el = this.closeTpl.overwrite(td, {"text": text});
27447         var close = el.getElementsByTagName("div")[0];
27448         var inner = el.getElementsByTagName("em")[0];
27449         return {"el": el, "close": close, "inner": inner};
27450     } else {
27451         if(!this.tabTpl){
27452             this.tabTpl = new Roo.Template(
27453                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27454                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27455             );
27456         }
27457         var el = this.tabTpl.overwrite(td, {"text": text});
27458         var inner = el.getElementsByTagName("em")[0];
27459         return {"el": el, "inner": inner};
27460     }
27461 };/*
27462  * Based on:
27463  * Ext JS Library 1.1.1
27464  * Copyright(c) 2006-2007, Ext JS, LLC.
27465  *
27466  * Originally Released Under LGPL - original licence link has changed is not relivant.
27467  *
27468  * Fork - LGPL
27469  * <script type="text/javascript">
27470  */
27471
27472 /**
27473  * @class Roo.Button
27474  * @extends Roo.util.Observable
27475  * Simple Button class
27476  * @cfg {String} text The button text
27477  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27478  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27479  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27480  * @cfg {Object} scope The scope of the handler
27481  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27482  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27483  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27484  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27485  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27486  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27487    applies if enableToggle = true)
27488  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27489  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27490   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27491  * @constructor
27492  * Create a new button
27493  * @param {Object} config The config object
27494  */
27495 Roo.Button = function(renderTo, config)
27496 {
27497     if (!config) {
27498         config = renderTo;
27499         renderTo = config.renderTo || false;
27500     }
27501     
27502     Roo.apply(this, config);
27503     this.addEvents({
27504         /**
27505              * @event click
27506              * Fires when this button is clicked
27507              * @param {Button} this
27508              * @param {EventObject} e The click event
27509              */
27510             "click" : true,
27511         /**
27512              * @event toggle
27513              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27514              * @param {Button} this
27515              * @param {Boolean} pressed
27516              */
27517             "toggle" : true,
27518         /**
27519              * @event mouseover
27520              * Fires when the mouse hovers over the button
27521              * @param {Button} this
27522              * @param {Event} e The event object
27523              */
27524         'mouseover' : true,
27525         /**
27526              * @event mouseout
27527              * Fires when the mouse exits the button
27528              * @param {Button} this
27529              * @param {Event} e The event object
27530              */
27531         'mouseout': true,
27532          /**
27533              * @event render
27534              * Fires when the button is rendered
27535              * @param {Button} this
27536              */
27537         'render': true
27538     });
27539     if(this.menu){
27540         this.menu = Roo.menu.MenuMgr.get(this.menu);
27541     }
27542     // register listeners first!!  - so render can be captured..
27543     Roo.util.Observable.call(this);
27544     if(renderTo){
27545         this.render(renderTo);
27546     }
27547     
27548   
27549 };
27550
27551 Roo.extend(Roo.Button, Roo.util.Observable, {
27552     /**
27553      * 
27554      */
27555     
27556     /**
27557      * Read-only. True if this button is hidden
27558      * @type Boolean
27559      */
27560     hidden : false,
27561     /**
27562      * Read-only. True if this button is disabled
27563      * @type Boolean
27564      */
27565     disabled : false,
27566     /**
27567      * Read-only. True if this button is pressed (only if enableToggle = true)
27568      * @type Boolean
27569      */
27570     pressed : false,
27571
27572     /**
27573      * @cfg {Number} tabIndex 
27574      * The DOM tabIndex for this button (defaults to undefined)
27575      */
27576     tabIndex : undefined,
27577
27578     /**
27579      * @cfg {Boolean} enableToggle
27580      * True to enable pressed/not pressed toggling (defaults to false)
27581      */
27582     enableToggle: false,
27583     /**
27584      * @cfg {Mixed} menu
27585      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27586      */
27587     menu : undefined,
27588     /**
27589      * @cfg {String} menuAlign
27590      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27591      */
27592     menuAlign : "tl-bl?",
27593
27594     /**
27595      * @cfg {String} iconCls
27596      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27597      */
27598     iconCls : undefined,
27599     /**
27600      * @cfg {String} type
27601      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27602      */
27603     type : 'button',
27604
27605     // private
27606     menuClassTarget: 'tr',
27607
27608     /**
27609      * @cfg {String} clickEvent
27610      * The type of event to map to the button's event handler (defaults to 'click')
27611      */
27612     clickEvent : 'click',
27613
27614     /**
27615      * @cfg {Boolean} handleMouseEvents
27616      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27617      */
27618     handleMouseEvents : true,
27619
27620     /**
27621      * @cfg {String} tooltipType
27622      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27623      */
27624     tooltipType : 'qtip',
27625
27626     /**
27627      * @cfg {String} cls
27628      * A CSS class to apply to the button's main element.
27629      */
27630     
27631     /**
27632      * @cfg {Roo.Template} template (Optional)
27633      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27634      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27635      * require code modifications if required elements (e.g. a button) aren't present.
27636      */
27637
27638     // private
27639     render : function(renderTo){
27640         var btn;
27641         if(this.hideParent){
27642             this.parentEl = Roo.get(renderTo);
27643         }
27644         if(!this.dhconfig){
27645             if(!this.template){
27646                 if(!Roo.Button.buttonTemplate){
27647                     // hideous table template
27648                     Roo.Button.buttonTemplate = new Roo.Template(
27649                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27650                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
27651                         "</tr></tbody></table>");
27652                 }
27653                 this.template = Roo.Button.buttonTemplate;
27654             }
27655             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27656             var btnEl = btn.child("button:first");
27657             btnEl.on('focus', this.onFocus, this);
27658             btnEl.on('blur', this.onBlur, this);
27659             if(this.cls){
27660                 btn.addClass(this.cls);
27661             }
27662             if(this.icon){
27663                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27664             }
27665             if(this.iconCls){
27666                 btnEl.addClass(this.iconCls);
27667                 if(!this.cls){
27668                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27669                 }
27670             }
27671             if(this.tabIndex !== undefined){
27672                 btnEl.dom.tabIndex = this.tabIndex;
27673             }
27674             if(this.tooltip){
27675                 if(typeof this.tooltip == 'object'){
27676                     Roo.QuickTips.tips(Roo.apply({
27677                           target: btnEl.id
27678                     }, this.tooltip));
27679                 } else {
27680                     btnEl.dom[this.tooltipType] = this.tooltip;
27681                 }
27682             }
27683         }else{
27684             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27685         }
27686         this.el = btn;
27687         if(this.id){
27688             this.el.dom.id = this.el.id = this.id;
27689         }
27690         if(this.menu){
27691             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27692             this.menu.on("show", this.onMenuShow, this);
27693             this.menu.on("hide", this.onMenuHide, this);
27694         }
27695         btn.addClass("x-btn");
27696         if(Roo.isIE && !Roo.isIE7){
27697             this.autoWidth.defer(1, this);
27698         }else{
27699             this.autoWidth();
27700         }
27701         if(this.handleMouseEvents){
27702             btn.on("mouseover", this.onMouseOver, this);
27703             btn.on("mouseout", this.onMouseOut, this);
27704             btn.on("mousedown", this.onMouseDown, this);
27705         }
27706         btn.on(this.clickEvent, this.onClick, this);
27707         //btn.on("mouseup", this.onMouseUp, this);
27708         if(this.hidden){
27709             this.hide();
27710         }
27711         if(this.disabled){
27712             this.disable();
27713         }
27714         Roo.ButtonToggleMgr.register(this);
27715         if(this.pressed){
27716             this.el.addClass("x-btn-pressed");
27717         }
27718         if(this.repeat){
27719             var repeater = new Roo.util.ClickRepeater(btn,
27720                 typeof this.repeat == "object" ? this.repeat : {}
27721             );
27722             repeater.on("click", this.onClick,  this);
27723         }
27724         
27725         this.fireEvent('render', this);
27726         
27727     },
27728     /**
27729      * Returns the button's underlying element
27730      * @return {Roo.Element} The element
27731      */
27732     getEl : function(){
27733         return this.el;  
27734     },
27735     
27736     /**
27737      * Destroys this Button and removes any listeners.
27738      */
27739     destroy : function(){
27740         Roo.ButtonToggleMgr.unregister(this);
27741         this.el.removeAllListeners();
27742         this.purgeListeners();
27743         this.el.remove();
27744     },
27745
27746     // private
27747     autoWidth : function(){
27748         if(this.el){
27749             this.el.setWidth("auto");
27750             if(Roo.isIE7 && Roo.isStrict){
27751                 var ib = this.el.child('button');
27752                 if(ib && ib.getWidth() > 20){
27753                     ib.clip();
27754                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27755                 }
27756             }
27757             if(this.minWidth){
27758                 if(this.hidden){
27759                     this.el.beginMeasure();
27760                 }
27761                 if(this.el.getWidth() < this.minWidth){
27762                     this.el.setWidth(this.minWidth);
27763                 }
27764                 if(this.hidden){
27765                     this.el.endMeasure();
27766                 }
27767             }
27768         }
27769     },
27770
27771     /**
27772      * Assigns this button's click handler
27773      * @param {Function} handler The function to call when the button is clicked
27774      * @param {Object} scope (optional) Scope for the function passed in
27775      */
27776     setHandler : function(handler, scope){
27777         this.handler = handler;
27778         this.scope = scope;  
27779     },
27780     
27781     /**
27782      * Sets this button's text
27783      * @param {String} text The button text
27784      */
27785     setText : function(text){
27786         this.text = text;
27787         if(this.el){
27788             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27789         }
27790         this.autoWidth();
27791     },
27792     
27793     /**
27794      * Gets the text for this button
27795      * @return {String} The button text
27796      */
27797     getText : function(){
27798         return this.text;  
27799     },
27800     
27801     /**
27802      * Show this button
27803      */
27804     show: function(){
27805         this.hidden = false;
27806         if(this.el){
27807             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27808         }
27809     },
27810     
27811     /**
27812      * Hide this button
27813      */
27814     hide: function(){
27815         this.hidden = true;
27816         if(this.el){
27817             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27818         }
27819     },
27820     
27821     /**
27822      * Convenience function for boolean show/hide
27823      * @param {Boolean} visible True to show, false to hide
27824      */
27825     setVisible: function(visible){
27826         if(visible) {
27827             this.show();
27828         }else{
27829             this.hide();
27830         }
27831     },
27832     
27833     /**
27834      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27835      * @param {Boolean} state (optional) Force a particular state
27836      */
27837     toggle : function(state){
27838         state = state === undefined ? !this.pressed : state;
27839         if(state != this.pressed){
27840             if(state){
27841                 this.el.addClass("x-btn-pressed");
27842                 this.pressed = true;
27843                 this.fireEvent("toggle", this, true);
27844             }else{
27845                 this.el.removeClass("x-btn-pressed");
27846                 this.pressed = false;
27847                 this.fireEvent("toggle", this, false);
27848             }
27849             if(this.toggleHandler){
27850                 this.toggleHandler.call(this.scope || this, this, state);
27851             }
27852         }
27853     },
27854     
27855     /**
27856      * Focus the button
27857      */
27858     focus : function(){
27859         this.el.child('button:first').focus();
27860     },
27861     
27862     /**
27863      * Disable this button
27864      */
27865     disable : function(){
27866         if(this.el){
27867             this.el.addClass("x-btn-disabled");
27868         }
27869         this.disabled = true;
27870     },
27871     
27872     /**
27873      * Enable this button
27874      */
27875     enable : function(){
27876         if(this.el){
27877             this.el.removeClass("x-btn-disabled");
27878         }
27879         this.disabled = false;
27880     },
27881
27882     /**
27883      * Convenience function for boolean enable/disable
27884      * @param {Boolean} enabled True to enable, false to disable
27885      */
27886     setDisabled : function(v){
27887         this[v !== true ? "enable" : "disable"]();
27888     },
27889
27890     // private
27891     onClick : function(e)
27892     {
27893         if(e){
27894             e.preventDefault();
27895         }
27896         if(e.button != 0){
27897             return;
27898         }
27899         if(!this.disabled){
27900             if(this.enableToggle){
27901                 this.toggle();
27902             }
27903             if(this.menu && !this.menu.isVisible()){
27904                 this.menu.show(this.el, this.menuAlign);
27905             }
27906             this.fireEvent("click", this, e);
27907             if(this.handler){
27908                 this.el.removeClass("x-btn-over");
27909                 this.handler.call(this.scope || this, this, e);
27910             }
27911         }
27912     },
27913     // private
27914     onMouseOver : function(e){
27915         if(!this.disabled){
27916             this.el.addClass("x-btn-over");
27917             this.fireEvent('mouseover', this, e);
27918         }
27919     },
27920     // private
27921     onMouseOut : function(e){
27922         if(!e.within(this.el,  true)){
27923             this.el.removeClass("x-btn-over");
27924             this.fireEvent('mouseout', this, e);
27925         }
27926     },
27927     // private
27928     onFocus : function(e){
27929         if(!this.disabled){
27930             this.el.addClass("x-btn-focus");
27931         }
27932     },
27933     // private
27934     onBlur : function(e){
27935         this.el.removeClass("x-btn-focus");
27936     },
27937     // private
27938     onMouseDown : function(e){
27939         if(!this.disabled && e.button == 0){
27940             this.el.addClass("x-btn-click");
27941             Roo.get(document).on('mouseup', this.onMouseUp, this);
27942         }
27943     },
27944     // private
27945     onMouseUp : function(e){
27946         if(e.button == 0){
27947             this.el.removeClass("x-btn-click");
27948             Roo.get(document).un('mouseup', this.onMouseUp, this);
27949         }
27950     },
27951     // private
27952     onMenuShow : function(e){
27953         this.el.addClass("x-btn-menu-active");
27954     },
27955     // private
27956     onMenuHide : function(e){
27957         this.el.removeClass("x-btn-menu-active");
27958     }   
27959 });
27960
27961 // Private utility class used by Button
27962 Roo.ButtonToggleMgr = function(){
27963    var groups = {};
27964    
27965    function toggleGroup(btn, state){
27966        if(state){
27967            var g = groups[btn.toggleGroup];
27968            for(var i = 0, l = g.length; i < l; i++){
27969                if(g[i] != btn){
27970                    g[i].toggle(false);
27971                }
27972            }
27973        }
27974    }
27975    
27976    return {
27977        register : function(btn){
27978            if(!btn.toggleGroup){
27979                return;
27980            }
27981            var g = groups[btn.toggleGroup];
27982            if(!g){
27983                g = groups[btn.toggleGroup] = [];
27984            }
27985            g.push(btn);
27986            btn.on("toggle", toggleGroup);
27987        },
27988        
27989        unregister : function(btn){
27990            if(!btn.toggleGroup){
27991                return;
27992            }
27993            var g = groups[btn.toggleGroup];
27994            if(g){
27995                g.remove(btn);
27996                btn.un("toggle", toggleGroup);
27997            }
27998        }
27999    };
28000 }();/*
28001  * Based on:
28002  * Ext JS Library 1.1.1
28003  * Copyright(c) 2006-2007, Ext JS, LLC.
28004  *
28005  * Originally Released Under LGPL - original licence link has changed is not relivant.
28006  *
28007  * Fork - LGPL
28008  * <script type="text/javascript">
28009  */
28010  
28011 /**
28012  * @class Roo.SplitButton
28013  * @extends Roo.Button
28014  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28015  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28016  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28017  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28018  * @cfg {String} arrowTooltip The title attribute of the arrow
28019  * @constructor
28020  * Create a new menu button
28021  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28022  * @param {Object} config The config object
28023  */
28024 Roo.SplitButton = function(renderTo, config){
28025     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28026     /**
28027      * @event arrowclick
28028      * Fires when this button's arrow is clicked
28029      * @param {SplitButton} this
28030      * @param {EventObject} e The click event
28031      */
28032     this.addEvents({"arrowclick":true});
28033 };
28034
28035 Roo.extend(Roo.SplitButton, Roo.Button, {
28036     render : function(renderTo){
28037         // this is one sweet looking template!
28038         var tpl = new Roo.Template(
28039             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28040             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28041             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
28042             "</tbody></table></td><td>",
28043             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28044             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
28045             "</tbody></table></td></tr></table>"
28046         );
28047         var btn = tpl.append(renderTo, [this.text, this.type], true);
28048         var btnEl = btn.child("button");
28049         if(this.cls){
28050             btn.addClass(this.cls);
28051         }
28052         if(this.icon){
28053             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28054         }
28055         if(this.iconCls){
28056             btnEl.addClass(this.iconCls);
28057             if(!this.cls){
28058                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28059             }
28060         }
28061         this.el = btn;
28062         if(this.handleMouseEvents){
28063             btn.on("mouseover", this.onMouseOver, this);
28064             btn.on("mouseout", this.onMouseOut, this);
28065             btn.on("mousedown", this.onMouseDown, this);
28066             btn.on("mouseup", this.onMouseUp, this);
28067         }
28068         btn.on(this.clickEvent, this.onClick, this);
28069         if(this.tooltip){
28070             if(typeof this.tooltip == 'object'){
28071                 Roo.QuickTips.tips(Roo.apply({
28072                       target: btnEl.id
28073                 }, this.tooltip));
28074             } else {
28075                 btnEl.dom[this.tooltipType] = this.tooltip;
28076             }
28077         }
28078         if(this.arrowTooltip){
28079             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28080         }
28081         if(this.hidden){
28082             this.hide();
28083         }
28084         if(this.disabled){
28085             this.disable();
28086         }
28087         if(this.pressed){
28088             this.el.addClass("x-btn-pressed");
28089         }
28090         if(Roo.isIE && !Roo.isIE7){
28091             this.autoWidth.defer(1, this);
28092         }else{
28093             this.autoWidth();
28094         }
28095         if(this.menu){
28096             this.menu.on("show", this.onMenuShow, this);
28097             this.menu.on("hide", this.onMenuHide, this);
28098         }
28099         this.fireEvent('render', this);
28100     },
28101
28102     // private
28103     autoWidth : function(){
28104         if(this.el){
28105             var tbl = this.el.child("table:first");
28106             var tbl2 = this.el.child("table:last");
28107             this.el.setWidth("auto");
28108             tbl.setWidth("auto");
28109             if(Roo.isIE7 && Roo.isStrict){
28110                 var ib = this.el.child('button:first');
28111                 if(ib && ib.getWidth() > 20){
28112                     ib.clip();
28113                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28114                 }
28115             }
28116             if(this.minWidth){
28117                 if(this.hidden){
28118                     this.el.beginMeasure();
28119                 }
28120                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28121                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28122                 }
28123                 if(this.hidden){
28124                     this.el.endMeasure();
28125                 }
28126             }
28127             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28128         } 
28129     },
28130     /**
28131      * Sets this button's click handler
28132      * @param {Function} handler The function to call when the button is clicked
28133      * @param {Object} scope (optional) Scope for the function passed above
28134      */
28135     setHandler : function(handler, scope){
28136         this.handler = handler;
28137         this.scope = scope;  
28138     },
28139     
28140     /**
28141      * Sets this button's arrow click handler
28142      * @param {Function} handler The function to call when the arrow is clicked
28143      * @param {Object} scope (optional) Scope for the function passed above
28144      */
28145     setArrowHandler : function(handler, scope){
28146         this.arrowHandler = handler;
28147         this.scope = scope;  
28148     },
28149     
28150     /**
28151      * Focus the button
28152      */
28153     focus : function(){
28154         if(this.el){
28155             this.el.child("button:first").focus();
28156         }
28157     },
28158
28159     // private
28160     onClick : function(e){
28161         e.preventDefault();
28162         if(!this.disabled){
28163             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28164                 if(this.menu && !this.menu.isVisible()){
28165                     this.menu.show(this.el, this.menuAlign);
28166                 }
28167                 this.fireEvent("arrowclick", this, e);
28168                 if(this.arrowHandler){
28169                     this.arrowHandler.call(this.scope || this, this, e);
28170                 }
28171             }else{
28172                 this.fireEvent("click", this, e);
28173                 if(this.handler){
28174                     this.handler.call(this.scope || this, this, e);
28175                 }
28176             }
28177         }
28178     },
28179     // private
28180     onMouseDown : function(e){
28181         if(!this.disabled){
28182             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28183         }
28184     },
28185     // private
28186     onMouseUp : function(e){
28187         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28188     }   
28189 });
28190
28191
28192 // backwards compat
28193 Roo.MenuButton = Roo.SplitButton;/*
28194  * Based on:
28195  * Ext JS Library 1.1.1
28196  * Copyright(c) 2006-2007, Ext JS, LLC.
28197  *
28198  * Originally Released Under LGPL - original licence link has changed is not relivant.
28199  *
28200  * Fork - LGPL
28201  * <script type="text/javascript">
28202  */
28203
28204 /**
28205  * @class Roo.Toolbar
28206  * Basic Toolbar class.
28207  * @constructor
28208  * Creates a new Toolbar
28209  * @param {Object} container The config object
28210  */ 
28211 Roo.Toolbar = function(container, buttons, config)
28212 {
28213     /// old consturctor format still supported..
28214     if(container instanceof Array){ // omit the container for later rendering
28215         buttons = container;
28216         config = buttons;
28217         container = null;
28218     }
28219     if (typeof(container) == 'object' && container.xtype) {
28220         config = container;
28221         container = config.container;
28222         buttons = config.buttons || []; // not really - use items!!
28223     }
28224     var xitems = [];
28225     if (config && config.items) {
28226         xitems = config.items;
28227         delete config.items;
28228     }
28229     Roo.apply(this, config);
28230     this.buttons = buttons;
28231     
28232     if(container){
28233         this.render(container);
28234     }
28235     this.xitems = xitems;
28236     Roo.each(xitems, function(b) {
28237         this.add(b);
28238     }, this);
28239     
28240 };
28241
28242 Roo.Toolbar.prototype = {
28243     /**
28244      * @cfg {Array} items
28245      * array of button configs or elements to add (will be converted to a MixedCollection)
28246      */
28247     
28248     /**
28249      * @cfg {String/HTMLElement/Element} container
28250      * The id or element that will contain the toolbar
28251      */
28252     // private
28253     render : function(ct){
28254         this.el = Roo.get(ct);
28255         if(this.cls){
28256             this.el.addClass(this.cls);
28257         }
28258         // using a table allows for vertical alignment
28259         // 100% width is needed by Safari...
28260         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28261         this.tr = this.el.child("tr", true);
28262         var autoId = 0;
28263         this.items = new Roo.util.MixedCollection(false, function(o){
28264             return o.id || ("item" + (++autoId));
28265         });
28266         if(this.buttons){
28267             this.add.apply(this, this.buttons);
28268             delete this.buttons;
28269         }
28270     },
28271
28272     /**
28273      * Adds element(s) to the toolbar -- this function takes a variable number of 
28274      * arguments of mixed type and adds them to the toolbar.
28275      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28276      * <ul>
28277      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28278      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28279      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28280      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28281      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28282      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28283      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28284      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28285      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28286      * </ul>
28287      * @param {Mixed} arg2
28288      * @param {Mixed} etc.
28289      */
28290     add : function(){
28291         var a = arguments, l = a.length;
28292         for(var i = 0; i < l; i++){
28293             this._add(a[i]);
28294         }
28295     },
28296     // private..
28297     _add : function(el) {
28298         
28299         if (el.xtype) {
28300             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28301         }
28302         
28303         if (el.applyTo){ // some kind of form field
28304             return this.addField(el);
28305         } 
28306         if (el.render){ // some kind of Toolbar.Item
28307             return this.addItem(el);
28308         }
28309         if (typeof el == "string"){ // string
28310             if(el == "separator" || el == "-"){
28311                 return this.addSeparator();
28312             }
28313             if (el == " "){
28314                 return this.addSpacer();
28315             }
28316             if(el == "->"){
28317                 return this.addFill();
28318             }
28319             return this.addText(el);
28320             
28321         }
28322         if(el.tagName){ // element
28323             return this.addElement(el);
28324         }
28325         if(typeof el == "object"){ // must be button config?
28326             return this.addButton(el);
28327         }
28328         // and now what?!?!
28329         return false;
28330         
28331     },
28332     
28333     /**
28334      * Add an Xtype element
28335      * @param {Object} xtype Xtype Object
28336      * @return {Object} created Object
28337      */
28338     addxtype : function(e){
28339         return this.add(e);  
28340     },
28341     
28342     /**
28343      * Returns the Element for this toolbar.
28344      * @return {Roo.Element}
28345      */
28346     getEl : function(){
28347         return this.el;  
28348     },
28349     
28350     /**
28351      * Adds a separator
28352      * @return {Roo.Toolbar.Item} The separator item
28353      */
28354     addSeparator : function(){
28355         return this.addItem(new Roo.Toolbar.Separator());
28356     },
28357
28358     /**
28359      * Adds a spacer element
28360      * @return {Roo.Toolbar.Spacer} The spacer item
28361      */
28362     addSpacer : function(){
28363         return this.addItem(new Roo.Toolbar.Spacer());
28364     },
28365
28366     /**
28367      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28368      * @return {Roo.Toolbar.Fill} The fill item
28369      */
28370     addFill : function(){
28371         return this.addItem(new Roo.Toolbar.Fill());
28372     },
28373
28374     /**
28375      * Adds any standard HTML element to the toolbar
28376      * @param {String/HTMLElement/Element} el The element or id of the element to add
28377      * @return {Roo.Toolbar.Item} The element's item
28378      */
28379     addElement : function(el){
28380         return this.addItem(new Roo.Toolbar.Item(el));
28381     },
28382     /**
28383      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28384      * @type Roo.util.MixedCollection  
28385      */
28386     items : false,
28387      
28388     /**
28389      * Adds any Toolbar.Item or subclass
28390      * @param {Roo.Toolbar.Item} item
28391      * @return {Roo.Toolbar.Item} The item
28392      */
28393     addItem : function(item){
28394         var td = this.nextBlock();
28395         item.render(td);
28396         this.items.add(item);
28397         return item;
28398     },
28399     
28400     /**
28401      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28402      * @param {Object/Array} config A button config or array of configs
28403      * @return {Roo.Toolbar.Button/Array}
28404      */
28405     addButton : function(config){
28406         if(config instanceof Array){
28407             var buttons = [];
28408             for(var i = 0, len = config.length; i < len; i++) {
28409                 buttons.push(this.addButton(config[i]));
28410             }
28411             return buttons;
28412         }
28413         var b = config;
28414         if(!(config instanceof Roo.Toolbar.Button)){
28415             b = config.split ?
28416                 new Roo.Toolbar.SplitButton(config) :
28417                 new Roo.Toolbar.Button(config);
28418         }
28419         var td = this.nextBlock();
28420         b.render(td);
28421         this.items.add(b);
28422         return b;
28423     },
28424     
28425     /**
28426      * Adds text to the toolbar
28427      * @param {String} text The text to add
28428      * @return {Roo.Toolbar.Item} The element's item
28429      */
28430     addText : function(text){
28431         return this.addItem(new Roo.Toolbar.TextItem(text));
28432     },
28433     
28434     /**
28435      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28436      * @param {Number} index The index where the item is to be inserted
28437      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28438      * @return {Roo.Toolbar.Button/Item}
28439      */
28440     insertButton : function(index, item){
28441         if(item instanceof Array){
28442             var buttons = [];
28443             for(var i = 0, len = item.length; i < len; i++) {
28444                buttons.push(this.insertButton(index + i, item[i]));
28445             }
28446             return buttons;
28447         }
28448         if (!(item instanceof Roo.Toolbar.Button)){
28449            item = new Roo.Toolbar.Button(item);
28450         }
28451         var td = document.createElement("td");
28452         this.tr.insertBefore(td, this.tr.childNodes[index]);
28453         item.render(td);
28454         this.items.insert(index, item);
28455         return item;
28456     },
28457     
28458     /**
28459      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28460      * @param {Object} config
28461      * @return {Roo.Toolbar.Item} The element's item
28462      */
28463     addDom : function(config, returnEl){
28464         var td = this.nextBlock();
28465         Roo.DomHelper.overwrite(td, config);
28466         var ti = new Roo.Toolbar.Item(td.firstChild);
28467         ti.render(td);
28468         this.items.add(ti);
28469         return ti;
28470     },
28471
28472     /**
28473      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28474      * @type Roo.util.MixedCollection  
28475      */
28476     fields : false,
28477     
28478     /**
28479      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28480      * Note: the field should not have been rendered yet. For a field that has already been
28481      * rendered, use {@link #addElement}.
28482      * @param {Roo.form.Field} field
28483      * @return {Roo.ToolbarItem}
28484      */
28485      
28486       
28487     addField : function(field) {
28488         if (!this.fields) {
28489             var autoId = 0;
28490             this.fields = new Roo.util.MixedCollection(false, function(o){
28491                 return o.id || ("item" + (++autoId));
28492             });
28493
28494         }
28495         
28496         var td = this.nextBlock();
28497         field.render(td);
28498         var ti = new Roo.Toolbar.Item(td.firstChild);
28499         ti.render(td);
28500         this.items.add(ti);
28501         this.fields.add(field);
28502         return ti;
28503     },
28504     /**
28505      * Hide the toolbar
28506      * @method hide
28507      */
28508      
28509       
28510     hide : function()
28511     {
28512         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28513         this.el.child('div').hide();
28514     },
28515     /**
28516      * Show the toolbar
28517      * @method show
28518      */
28519     show : function()
28520     {
28521         this.el.child('div').show();
28522     },
28523       
28524     // private
28525     nextBlock : function(){
28526         var td = document.createElement("td");
28527         this.tr.appendChild(td);
28528         return td;
28529     },
28530
28531     // private
28532     destroy : function(){
28533         if(this.items){ // rendered?
28534             Roo.destroy.apply(Roo, this.items.items);
28535         }
28536         if(this.fields){ // rendered?
28537             Roo.destroy.apply(Roo, this.fields.items);
28538         }
28539         Roo.Element.uncache(this.el, this.tr);
28540     }
28541 };
28542
28543 /**
28544  * @class Roo.Toolbar.Item
28545  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28546  * @constructor
28547  * Creates a new Item
28548  * @param {HTMLElement} el 
28549  */
28550 Roo.Toolbar.Item = function(el){
28551     var cfg = {};
28552     if (typeof (el.xtype) != 'undefined') {
28553         cfg = el;
28554         el = cfg.el;
28555     }
28556     
28557     this.el = Roo.getDom(el);
28558     this.id = Roo.id(this.el);
28559     this.hidden = false;
28560     
28561     this.addEvents({
28562          /**
28563              * @event render
28564              * Fires when the button is rendered
28565              * @param {Button} this
28566              */
28567         'render': true
28568     });
28569     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28570 };
28571 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28572 //Roo.Toolbar.Item.prototype = {
28573     
28574     /**
28575      * Get this item's HTML Element
28576      * @return {HTMLElement}
28577      */
28578     getEl : function(){
28579        return this.el;  
28580     },
28581
28582     // private
28583     render : function(td){
28584         
28585          this.td = td;
28586         td.appendChild(this.el);
28587         
28588         this.fireEvent('render', this);
28589     },
28590     
28591     /**
28592      * Removes and destroys this item.
28593      */
28594     destroy : function(){
28595         this.td.parentNode.removeChild(this.td);
28596     },
28597     
28598     /**
28599      * Shows this item.
28600      */
28601     show: function(){
28602         this.hidden = false;
28603         this.td.style.display = "";
28604     },
28605     
28606     /**
28607      * Hides this item.
28608      */
28609     hide: function(){
28610         this.hidden = true;
28611         this.td.style.display = "none";
28612     },
28613     
28614     /**
28615      * Convenience function for boolean show/hide.
28616      * @param {Boolean} visible true to show/false to hide
28617      */
28618     setVisible: function(visible){
28619         if(visible) {
28620             this.show();
28621         }else{
28622             this.hide();
28623         }
28624     },
28625     
28626     /**
28627      * Try to focus this item.
28628      */
28629     focus : function(){
28630         Roo.fly(this.el).focus();
28631     },
28632     
28633     /**
28634      * Disables this item.
28635      */
28636     disable : function(){
28637         Roo.fly(this.td).addClass("x-item-disabled");
28638         this.disabled = true;
28639         this.el.disabled = true;
28640     },
28641     
28642     /**
28643      * Enables this item.
28644      */
28645     enable : function(){
28646         Roo.fly(this.td).removeClass("x-item-disabled");
28647         this.disabled = false;
28648         this.el.disabled = false;
28649     }
28650 });
28651
28652
28653 /**
28654  * @class Roo.Toolbar.Separator
28655  * @extends Roo.Toolbar.Item
28656  * A simple toolbar separator class
28657  * @constructor
28658  * Creates a new Separator
28659  */
28660 Roo.Toolbar.Separator = function(cfg){
28661     
28662     var s = document.createElement("span");
28663     s.className = "ytb-sep";
28664     if (cfg) {
28665         cfg.el = s;
28666     }
28667     
28668     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28669 };
28670 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28671     enable:Roo.emptyFn,
28672     disable:Roo.emptyFn,
28673     focus:Roo.emptyFn
28674 });
28675
28676 /**
28677  * @class Roo.Toolbar.Spacer
28678  * @extends Roo.Toolbar.Item
28679  * A simple element that adds extra horizontal space to a toolbar.
28680  * @constructor
28681  * Creates a new Spacer
28682  */
28683 Roo.Toolbar.Spacer = function(cfg){
28684     var s = document.createElement("div");
28685     s.className = "ytb-spacer";
28686     if (cfg) {
28687         cfg.el = s;
28688     }
28689     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28690 };
28691 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28692     enable:Roo.emptyFn,
28693     disable:Roo.emptyFn,
28694     focus:Roo.emptyFn
28695 });
28696
28697 /**
28698  * @class Roo.Toolbar.Fill
28699  * @extends Roo.Toolbar.Spacer
28700  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28701  * @constructor
28702  * Creates a new Spacer
28703  */
28704 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28705     // private
28706     render : function(td){
28707         td.style.width = '100%';
28708         Roo.Toolbar.Fill.superclass.render.call(this, td);
28709     }
28710 });
28711
28712 /**
28713  * @class Roo.Toolbar.TextItem
28714  * @extends Roo.Toolbar.Item
28715  * A simple class that renders text directly into a toolbar.
28716  * @constructor
28717  * Creates a new TextItem
28718  * @param {String} text
28719  */
28720 Roo.Toolbar.TextItem = function(cfg){
28721     var  text = cfg || "";
28722     if (typeof(cfg) == 'object') {
28723         text = cfg.text || "";
28724     }  else {
28725         cfg = null;
28726     }
28727     var s = document.createElement("span");
28728     s.className = "ytb-text";
28729     s.innerHTML = text;
28730     if (cfg) {
28731         cfg.el  = s;
28732     }
28733     
28734     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28735 };
28736 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28737     
28738      
28739     enable:Roo.emptyFn,
28740     disable:Roo.emptyFn,
28741     focus:Roo.emptyFn
28742 });
28743
28744 /**
28745  * @class Roo.Toolbar.Button
28746  * @extends Roo.Button
28747  * A button that renders into a toolbar.
28748  * @constructor
28749  * Creates a new Button
28750  * @param {Object} config A standard {@link Roo.Button} config object
28751  */
28752 Roo.Toolbar.Button = function(config){
28753     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28754 };
28755 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28756     render : function(td){
28757         this.td = td;
28758         Roo.Toolbar.Button.superclass.render.call(this, td);
28759     },
28760     
28761     /**
28762      * Removes and destroys this button
28763      */
28764     destroy : function(){
28765         Roo.Toolbar.Button.superclass.destroy.call(this);
28766         this.td.parentNode.removeChild(this.td);
28767     },
28768     
28769     /**
28770      * Shows this button
28771      */
28772     show: function(){
28773         this.hidden = false;
28774         this.td.style.display = "";
28775     },
28776     
28777     /**
28778      * Hides this button
28779      */
28780     hide: function(){
28781         this.hidden = true;
28782         this.td.style.display = "none";
28783     },
28784
28785     /**
28786      * Disables this item
28787      */
28788     disable : function(){
28789         Roo.fly(this.td).addClass("x-item-disabled");
28790         this.disabled = true;
28791     },
28792
28793     /**
28794      * Enables this item
28795      */
28796     enable : function(){
28797         Roo.fly(this.td).removeClass("x-item-disabled");
28798         this.disabled = false;
28799     }
28800 });
28801 // backwards compat
28802 Roo.ToolbarButton = Roo.Toolbar.Button;
28803
28804 /**
28805  * @class Roo.Toolbar.SplitButton
28806  * @extends Roo.SplitButton
28807  * A menu button that renders into a toolbar.
28808  * @constructor
28809  * Creates a new SplitButton
28810  * @param {Object} config A standard {@link Roo.SplitButton} config object
28811  */
28812 Roo.Toolbar.SplitButton = function(config){
28813     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28814 };
28815 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28816     render : function(td){
28817         this.td = td;
28818         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28819     },
28820     
28821     /**
28822      * Removes and destroys this button
28823      */
28824     destroy : function(){
28825         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28826         this.td.parentNode.removeChild(this.td);
28827     },
28828     
28829     /**
28830      * Shows this button
28831      */
28832     show: function(){
28833         this.hidden = false;
28834         this.td.style.display = "";
28835     },
28836     
28837     /**
28838      * Hides this button
28839      */
28840     hide: function(){
28841         this.hidden = true;
28842         this.td.style.display = "none";
28843     }
28844 });
28845
28846 // backwards compat
28847 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28848  * Based on:
28849  * Ext JS Library 1.1.1
28850  * Copyright(c) 2006-2007, Ext JS, LLC.
28851  *
28852  * Originally Released Under LGPL - original licence link has changed is not relivant.
28853  *
28854  * Fork - LGPL
28855  * <script type="text/javascript">
28856  */
28857  
28858 /**
28859  * @class Roo.PagingToolbar
28860  * @extends Roo.Toolbar
28861  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28862  * @constructor
28863  * Create a new PagingToolbar
28864  * @param {Object} config The config object
28865  */
28866 Roo.PagingToolbar = function(el, ds, config)
28867 {
28868     // old args format still supported... - xtype is prefered..
28869     if (typeof(el) == 'object' && el.xtype) {
28870         // created from xtype...
28871         config = el;
28872         ds = el.dataSource;
28873         el = config.container;
28874     }
28875     var items = [];
28876     if (config.items) {
28877         items = config.items;
28878         config.items = [];
28879     }
28880     
28881     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28882     this.ds = ds;
28883     this.cursor = 0;
28884     this.renderButtons(this.el);
28885     this.bind(ds);
28886     
28887     // supprot items array.
28888    
28889     Roo.each(items, function(e) {
28890         this.add(Roo.factory(e));
28891     },this);
28892     
28893 };
28894
28895 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28896     /**
28897      * @cfg {Roo.data.Store} dataSource
28898      * The underlying data store providing the paged data
28899      */
28900     /**
28901      * @cfg {String/HTMLElement/Element} container
28902      * container The id or element that will contain the toolbar
28903      */
28904     /**
28905      * @cfg {Boolean} displayInfo
28906      * True to display the displayMsg (defaults to false)
28907      */
28908     /**
28909      * @cfg {Number} pageSize
28910      * The number of records to display per page (defaults to 20)
28911      */
28912     pageSize: 20,
28913     /**
28914      * @cfg {String} displayMsg
28915      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28916      */
28917     displayMsg : 'Displaying {0} - {1} of {2}',
28918     /**
28919      * @cfg {String} emptyMsg
28920      * The message to display when no records are found (defaults to "No data to display")
28921      */
28922     emptyMsg : 'No data to display',
28923     /**
28924      * Customizable piece of the default paging text (defaults to "Page")
28925      * @type String
28926      */
28927     beforePageText : "Page",
28928     /**
28929      * Customizable piece of the default paging text (defaults to "of %0")
28930      * @type String
28931      */
28932     afterPageText : "of {0}",
28933     /**
28934      * Customizable piece of the default paging text (defaults to "First Page")
28935      * @type String
28936      */
28937     firstText : "First Page",
28938     /**
28939      * Customizable piece of the default paging text (defaults to "Previous Page")
28940      * @type String
28941      */
28942     prevText : "Previous Page",
28943     /**
28944      * Customizable piece of the default paging text (defaults to "Next Page")
28945      * @type String
28946      */
28947     nextText : "Next Page",
28948     /**
28949      * Customizable piece of the default paging text (defaults to "Last Page")
28950      * @type String
28951      */
28952     lastText : "Last Page",
28953     /**
28954      * Customizable piece of the default paging text (defaults to "Refresh")
28955      * @type String
28956      */
28957     refreshText : "Refresh",
28958
28959     // private
28960     renderButtons : function(el){
28961         Roo.PagingToolbar.superclass.render.call(this, el);
28962         this.first = this.addButton({
28963             tooltip: this.firstText,
28964             cls: "x-btn-icon x-grid-page-first",
28965             disabled: true,
28966             handler: this.onClick.createDelegate(this, ["first"])
28967         });
28968         this.prev = this.addButton({
28969             tooltip: this.prevText,
28970             cls: "x-btn-icon x-grid-page-prev",
28971             disabled: true,
28972             handler: this.onClick.createDelegate(this, ["prev"])
28973         });
28974         //this.addSeparator();
28975         this.add(this.beforePageText);
28976         this.field = Roo.get(this.addDom({
28977            tag: "input",
28978            type: "text",
28979            size: "3",
28980            value: "1",
28981            cls: "x-grid-page-number"
28982         }).el);
28983         this.field.on("keydown", this.onPagingKeydown, this);
28984         this.field.on("focus", function(){this.dom.select();});
28985         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28986         this.field.setHeight(18);
28987         //this.addSeparator();
28988         this.next = this.addButton({
28989             tooltip: this.nextText,
28990             cls: "x-btn-icon x-grid-page-next",
28991             disabled: true,
28992             handler: this.onClick.createDelegate(this, ["next"])
28993         });
28994         this.last = this.addButton({
28995             tooltip: this.lastText,
28996             cls: "x-btn-icon x-grid-page-last",
28997             disabled: true,
28998             handler: this.onClick.createDelegate(this, ["last"])
28999         });
29000         //this.addSeparator();
29001         this.loading = this.addButton({
29002             tooltip: this.refreshText,
29003             cls: "x-btn-icon x-grid-loading",
29004             handler: this.onClick.createDelegate(this, ["refresh"])
29005         });
29006
29007         if(this.displayInfo){
29008             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
29009         }
29010     },
29011
29012     // private
29013     updateInfo : function(){
29014         if(this.displayEl){
29015             var count = this.ds.getCount();
29016             var msg = count == 0 ?
29017                 this.emptyMsg :
29018                 String.format(
29019                     this.displayMsg,
29020                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29021                 );
29022             this.displayEl.update(msg);
29023         }
29024     },
29025
29026     // private
29027     onLoad : function(ds, r, o){
29028        this.cursor = o.params ? o.params.start : 0;
29029        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29030
29031        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29032        this.field.dom.value = ap;
29033        this.first.setDisabled(ap == 1);
29034        this.prev.setDisabled(ap == 1);
29035        this.next.setDisabled(ap == ps);
29036        this.last.setDisabled(ap == ps);
29037        this.loading.enable();
29038        this.updateInfo();
29039     },
29040
29041     // private
29042     getPageData : function(){
29043         var total = this.ds.getTotalCount();
29044         return {
29045             total : total,
29046             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29047             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29048         };
29049     },
29050
29051     // private
29052     onLoadError : function(){
29053         this.loading.enable();
29054     },
29055
29056     // private
29057     onPagingKeydown : function(e){
29058         var k = e.getKey();
29059         var d = this.getPageData();
29060         if(k == e.RETURN){
29061             var v = this.field.dom.value, pageNum;
29062             if(!v || isNaN(pageNum = parseInt(v, 10))){
29063                 this.field.dom.value = d.activePage;
29064                 return;
29065             }
29066             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29067             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29068             e.stopEvent();
29069         }
29070         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
29071         {
29072           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29073           this.field.dom.value = pageNum;
29074           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29075           e.stopEvent();
29076         }
29077         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29078         {
29079           var v = this.field.dom.value, pageNum; 
29080           var increment = (e.shiftKey) ? 10 : 1;
29081           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29082             increment *= -1;
29083           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29084             this.field.dom.value = d.activePage;
29085             return;
29086           }
29087           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29088           {
29089             this.field.dom.value = parseInt(v, 10) + increment;
29090             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29091             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29092           }
29093           e.stopEvent();
29094         }
29095     },
29096
29097     // private
29098     beforeLoad : function(){
29099         if(this.loading){
29100             this.loading.disable();
29101         }
29102     },
29103
29104     // private
29105     onClick : function(which){
29106         var ds = this.ds;
29107         switch(which){
29108             case "first":
29109                 ds.load({params:{start: 0, limit: this.pageSize}});
29110             break;
29111             case "prev":
29112                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29113             break;
29114             case "next":
29115                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29116             break;
29117             case "last":
29118                 var total = ds.getTotalCount();
29119                 var extra = total % this.pageSize;
29120                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29121                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29122             break;
29123             case "refresh":
29124                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29125             break;
29126         }
29127     },
29128
29129     /**
29130      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29131      * @param {Roo.data.Store} store The data store to unbind
29132      */
29133     unbind : function(ds){
29134         ds.un("beforeload", this.beforeLoad, this);
29135         ds.un("load", this.onLoad, this);
29136         ds.un("loadexception", this.onLoadError, this);
29137         ds.un("remove", this.updateInfo, this);
29138         ds.un("add", this.updateInfo, this);
29139         this.ds = undefined;
29140     },
29141
29142     /**
29143      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29144      * @param {Roo.data.Store} store The data store to bind
29145      */
29146     bind : function(ds){
29147         ds.on("beforeload", this.beforeLoad, this);
29148         ds.on("load", this.onLoad, this);
29149         ds.on("loadexception", this.onLoadError, this);
29150         ds.on("remove", this.updateInfo, this);
29151         ds.on("add", this.updateInfo, this);
29152         this.ds = ds;
29153     }
29154 });/*
29155  * Based on:
29156  * Ext JS Library 1.1.1
29157  * Copyright(c) 2006-2007, Ext JS, LLC.
29158  *
29159  * Originally Released Under LGPL - original licence link has changed is not relivant.
29160  *
29161  * Fork - LGPL
29162  * <script type="text/javascript">
29163  */
29164
29165 /**
29166  * @class Roo.Resizable
29167  * @extends Roo.util.Observable
29168  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29169  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29170  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
29171  * the element will be wrapped for you automatically.</p>
29172  * <p>Here is the list of valid resize handles:</p>
29173  * <pre>
29174 Value   Description
29175 ------  -------------------
29176  'n'     north
29177  's'     south
29178  'e'     east
29179  'w'     west
29180  'nw'    northwest
29181  'sw'    southwest
29182  'se'    southeast
29183  'ne'    northeast
29184  'hd'    horizontal drag
29185  'all'   all
29186 </pre>
29187  * <p>Here's an example showing the creation of a typical Resizable:</p>
29188  * <pre><code>
29189 var resizer = new Roo.Resizable("element-id", {
29190     handles: 'all',
29191     minWidth: 200,
29192     minHeight: 100,
29193     maxWidth: 500,
29194     maxHeight: 400,
29195     pinned: true
29196 });
29197 resizer.on("resize", myHandler);
29198 </code></pre>
29199  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29200  * resizer.east.setDisplayed(false);</p>
29201  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29202  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29203  * resize operation's new size (defaults to [0, 0])
29204  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29205  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29206  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29207  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29208  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29209  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29210  * @cfg {Number} width The width of the element in pixels (defaults to null)
29211  * @cfg {Number} height The height of the element in pixels (defaults to null)
29212  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29213  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29214  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29215  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29216  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29217  * in favor of the handles config option (defaults to false)
29218  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29219  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29220  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29221  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29222  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29223  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29224  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29225  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29226  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29227  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29228  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29229  * @constructor
29230  * Create a new resizable component
29231  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29232  * @param {Object} config configuration options
29233   */
29234 Roo.Resizable = function(el, config)
29235 {
29236     this.el = Roo.get(el);
29237
29238     if(config && config.wrap){
29239         config.resizeChild = this.el;
29240         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29241         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29242         this.el.setStyle("overflow", "hidden");
29243         this.el.setPositioning(config.resizeChild.getPositioning());
29244         config.resizeChild.clearPositioning();
29245         if(!config.width || !config.height){
29246             var csize = config.resizeChild.getSize();
29247             this.el.setSize(csize.width, csize.height);
29248         }
29249         if(config.pinned && !config.adjustments){
29250             config.adjustments = "auto";
29251         }
29252     }
29253
29254     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29255     this.proxy.unselectable();
29256     this.proxy.enableDisplayMode('block');
29257
29258     Roo.apply(this, config);
29259
29260     if(this.pinned){
29261         this.disableTrackOver = true;
29262         this.el.addClass("x-resizable-pinned");
29263     }
29264     // if the element isn't positioned, make it relative
29265     var position = this.el.getStyle("position");
29266     if(position != "absolute" && position != "fixed"){
29267         this.el.setStyle("position", "relative");
29268     }
29269     if(!this.handles){ // no handles passed, must be legacy style
29270         this.handles = 's,e,se';
29271         if(this.multiDirectional){
29272             this.handles += ',n,w';
29273         }
29274     }
29275     if(this.handles == "all"){
29276         this.handles = "n s e w ne nw se sw";
29277     }
29278     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29279     var ps = Roo.Resizable.positions;
29280     for(var i = 0, len = hs.length; i < len; i++){
29281         if(hs[i] && ps[hs[i]]){
29282             var pos = ps[hs[i]];
29283             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29284         }
29285     }
29286     // legacy
29287     this.corner = this.southeast;
29288     
29289     // updateBox = the box can move..
29290     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29291         this.updateBox = true;
29292     }
29293
29294     this.activeHandle = null;
29295
29296     if(this.resizeChild){
29297         if(typeof this.resizeChild == "boolean"){
29298             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29299         }else{
29300             this.resizeChild = Roo.get(this.resizeChild, true);
29301         }
29302     }
29303     
29304     if(this.adjustments == "auto"){
29305         var rc = this.resizeChild;
29306         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29307         if(rc && (hw || hn)){
29308             rc.position("relative");
29309             rc.setLeft(hw ? hw.el.getWidth() : 0);
29310             rc.setTop(hn ? hn.el.getHeight() : 0);
29311         }
29312         this.adjustments = [
29313             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29314             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29315         ];
29316     }
29317
29318     if(this.draggable){
29319         this.dd = this.dynamic ?
29320             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29321         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29322     }
29323
29324     // public events
29325     this.addEvents({
29326         /**
29327          * @event beforeresize
29328          * Fired before resize is allowed. Set enabled to false to cancel resize.
29329          * @param {Roo.Resizable} this
29330          * @param {Roo.EventObject} e The mousedown event
29331          */
29332         "beforeresize" : true,
29333         /**
29334          * @event resizing
29335          * Fired a resizing.
29336          * @param {Roo.Resizable} this
29337          * @param {Number} x The new x position
29338          * @param {Number} y The new y position
29339          * @param {Number} w The new w width
29340          * @param {Number} h The new h hight
29341          * @param {Roo.EventObject} e The mouseup event
29342          */
29343         "resizing" : true,
29344         /**
29345          * @event resize
29346          * Fired after a resize.
29347          * @param {Roo.Resizable} this
29348          * @param {Number} width The new width
29349          * @param {Number} height The new height
29350          * @param {Roo.EventObject} e The mouseup event
29351          */
29352         "resize" : true
29353     });
29354
29355     if(this.width !== null && this.height !== null){
29356         this.resizeTo(this.width, this.height);
29357     }else{
29358         this.updateChildSize();
29359     }
29360     if(Roo.isIE){
29361         this.el.dom.style.zoom = 1;
29362     }
29363     Roo.Resizable.superclass.constructor.call(this);
29364 };
29365
29366 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29367         resizeChild : false,
29368         adjustments : [0, 0],
29369         minWidth : 5,
29370         minHeight : 5,
29371         maxWidth : 10000,
29372         maxHeight : 10000,
29373         enabled : true,
29374         animate : false,
29375         duration : .35,
29376         dynamic : false,
29377         handles : false,
29378         multiDirectional : false,
29379         disableTrackOver : false,
29380         easing : 'easeOutStrong',
29381         widthIncrement : 0,
29382         heightIncrement : 0,
29383         pinned : false,
29384         width : null,
29385         height : null,
29386         preserveRatio : false,
29387         transparent: false,
29388         minX: 0,
29389         minY: 0,
29390         draggable: false,
29391
29392         /**
29393          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29394          */
29395         constrainTo: undefined,
29396         /**
29397          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29398          */
29399         resizeRegion: undefined,
29400
29401
29402     /**
29403      * Perform a manual resize
29404      * @param {Number} width
29405      * @param {Number} height
29406      */
29407     resizeTo : function(width, height){
29408         this.el.setSize(width, height);
29409         this.updateChildSize();
29410         this.fireEvent("resize", this, width, height, null);
29411     },
29412
29413     // private
29414     startSizing : function(e, handle){
29415         this.fireEvent("beforeresize", this, e);
29416         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29417
29418             if(!this.overlay){
29419                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29420                 this.overlay.unselectable();
29421                 this.overlay.enableDisplayMode("block");
29422                 this.overlay.on("mousemove", this.onMouseMove, this);
29423                 this.overlay.on("mouseup", this.onMouseUp, this);
29424             }
29425             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29426
29427             this.resizing = true;
29428             this.startBox = this.el.getBox();
29429             this.startPoint = e.getXY();
29430             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29431                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29432
29433             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29434             this.overlay.show();
29435
29436             if(this.constrainTo) {
29437                 var ct = Roo.get(this.constrainTo);
29438                 this.resizeRegion = ct.getRegion().adjust(
29439                     ct.getFrameWidth('t'),
29440                     ct.getFrameWidth('l'),
29441                     -ct.getFrameWidth('b'),
29442                     -ct.getFrameWidth('r')
29443                 );
29444             }
29445
29446             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29447             this.proxy.show();
29448             this.proxy.setBox(this.startBox);
29449             if(!this.dynamic){
29450                 this.proxy.setStyle('visibility', 'visible');
29451             }
29452         }
29453     },
29454
29455     // private
29456     onMouseDown : function(handle, e){
29457         if(this.enabled){
29458             e.stopEvent();
29459             this.activeHandle = handle;
29460             this.startSizing(e, handle);
29461         }
29462     },
29463
29464     // private
29465     onMouseUp : function(e){
29466         var size = this.resizeElement();
29467         this.resizing = false;
29468         this.handleOut();
29469         this.overlay.hide();
29470         this.proxy.hide();
29471         this.fireEvent("resize", this, size.width, size.height, e);
29472     },
29473
29474     // private
29475     updateChildSize : function(){
29476         
29477         if(this.resizeChild){
29478             var el = this.el;
29479             var child = this.resizeChild;
29480             var adj = this.adjustments;
29481             if(el.dom.offsetWidth){
29482                 var b = el.getSize(true);
29483                 child.setSize(b.width+adj[0], b.height+adj[1]);
29484             }
29485             // Second call here for IE
29486             // The first call enables instant resizing and
29487             // the second call corrects scroll bars if they
29488             // exist
29489             if(Roo.isIE){
29490                 setTimeout(function(){
29491                     if(el.dom.offsetWidth){
29492                         var b = el.getSize(true);
29493                         child.setSize(b.width+adj[0], b.height+adj[1]);
29494                     }
29495                 }, 10);
29496             }
29497         }
29498     },
29499
29500     // private
29501     snap : function(value, inc, min){
29502         if(!inc || !value) return value;
29503         var newValue = value;
29504         var m = value % inc;
29505         if(m > 0){
29506             if(m > (inc/2)){
29507                 newValue = value + (inc-m);
29508             }else{
29509                 newValue = value - m;
29510             }
29511         }
29512         return Math.max(min, newValue);
29513     },
29514
29515     // private
29516     resizeElement : function(){
29517         var box = this.proxy.getBox();
29518         if(this.updateBox){
29519             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29520         }else{
29521             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29522         }
29523         this.updateChildSize();
29524         if(!this.dynamic){
29525             this.proxy.hide();
29526         }
29527         return box;
29528     },
29529
29530     // private
29531     constrain : function(v, diff, m, mx){
29532         if(v - diff < m){
29533             diff = v - m;
29534         }else if(v - diff > mx){
29535             diff = mx - v;
29536         }
29537         return diff;
29538     },
29539
29540     // private
29541     onMouseMove : function(e){
29542         
29543         if(this.enabled){
29544             try{// try catch so if something goes wrong the user doesn't get hung
29545
29546             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29547                 return;
29548             }
29549
29550             //var curXY = this.startPoint;
29551             var curSize = this.curSize || this.startBox;
29552             var x = this.startBox.x, y = this.startBox.y;
29553             var ox = x, oy = y;
29554             var w = curSize.width, h = curSize.height;
29555             var ow = w, oh = h;
29556             var mw = this.minWidth, mh = this.minHeight;
29557             var mxw = this.maxWidth, mxh = this.maxHeight;
29558             var wi = this.widthIncrement;
29559             var hi = this.heightIncrement;
29560
29561             var eventXY = e.getXY();
29562             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29563             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29564
29565             var pos = this.activeHandle.position;
29566
29567             switch(pos){
29568                 case "east":
29569                     w += diffX;
29570                     w = Math.min(Math.max(mw, w), mxw);
29571                     break;
29572              
29573                 case "south":
29574                     h += diffY;
29575                     h = Math.min(Math.max(mh, h), mxh);
29576                     break;
29577                 case "southeast":
29578                     w += diffX;
29579                     h += diffY;
29580                     w = Math.min(Math.max(mw, w), mxw);
29581                     h = Math.min(Math.max(mh, h), mxh);
29582                     break;
29583                 case "north":
29584                     diffY = this.constrain(h, diffY, mh, mxh);
29585                     y += diffY;
29586                     h -= diffY;
29587                     break;
29588                 case "hdrag":
29589                     
29590                     if (wi) {
29591                         var adiffX = Math.abs(diffX);
29592                         var sub = (adiffX % wi); // how much 
29593                         if (sub > (wi/2)) { // far enough to snap
29594                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29595                         } else {
29596                             // remove difference.. 
29597                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29598                         }
29599                     }
29600                     x += diffX;
29601                     x = Math.max(this.minX, x);
29602                     break;
29603                 case "west":
29604                     diffX = this.constrain(w, diffX, mw, mxw);
29605                     x += diffX;
29606                     w -= diffX;
29607                     break;
29608                 case "northeast":
29609                     w += diffX;
29610                     w = Math.min(Math.max(mw, w), mxw);
29611                     diffY = this.constrain(h, diffY, mh, mxh);
29612                     y += diffY;
29613                     h -= diffY;
29614                     break;
29615                 case "northwest":
29616                     diffX = this.constrain(w, diffX, mw, mxw);
29617                     diffY = this.constrain(h, diffY, mh, mxh);
29618                     y += diffY;
29619                     h -= diffY;
29620                     x += diffX;
29621                     w -= diffX;
29622                     break;
29623                case "southwest":
29624                     diffX = this.constrain(w, diffX, mw, mxw);
29625                     h += diffY;
29626                     h = Math.min(Math.max(mh, h), mxh);
29627                     x += diffX;
29628                     w -= diffX;
29629                     break;
29630             }
29631
29632             var sw = this.snap(w, wi, mw);
29633             var sh = this.snap(h, hi, mh);
29634             if(sw != w || sh != h){
29635                 switch(pos){
29636                     case "northeast":
29637                         y -= sh - h;
29638                     break;
29639                     case "north":
29640                         y -= sh - h;
29641                         break;
29642                     case "southwest":
29643                         x -= sw - w;
29644                     break;
29645                     case "west":
29646                         x -= sw - w;
29647                         break;
29648                     case "northwest":
29649                         x -= sw - w;
29650                         y -= sh - h;
29651                     break;
29652                 }
29653                 w = sw;
29654                 h = sh;
29655             }
29656
29657             if(this.preserveRatio){
29658                 switch(pos){
29659                     case "southeast":
29660                     case "east":
29661                         h = oh * (w/ow);
29662                         h = Math.min(Math.max(mh, h), mxh);
29663                         w = ow * (h/oh);
29664                        break;
29665                     case "south":
29666                         w = ow * (h/oh);
29667                         w = Math.min(Math.max(mw, w), mxw);
29668                         h = oh * (w/ow);
29669                         break;
29670                     case "northeast":
29671                         w = ow * (h/oh);
29672                         w = Math.min(Math.max(mw, w), mxw);
29673                         h = oh * (w/ow);
29674                     break;
29675                     case "north":
29676                         var tw = w;
29677                         w = ow * (h/oh);
29678                         w = Math.min(Math.max(mw, w), mxw);
29679                         h = oh * (w/ow);
29680                         x += (tw - w) / 2;
29681                         break;
29682                     case "southwest":
29683                         h = oh * (w/ow);
29684                         h = Math.min(Math.max(mh, h), mxh);
29685                         var tw = w;
29686                         w = ow * (h/oh);
29687                         x += tw - w;
29688                         break;
29689                     case "west":
29690                         var th = h;
29691                         h = oh * (w/ow);
29692                         h = Math.min(Math.max(mh, h), mxh);
29693                         y += (th - h) / 2;
29694                         var tw = w;
29695                         w = ow * (h/oh);
29696                         x += tw - w;
29697                        break;
29698                     case "northwest":
29699                         var tw = w;
29700                         var th = h;
29701                         h = oh * (w/ow);
29702                         h = Math.min(Math.max(mh, h), mxh);
29703                         w = ow * (h/oh);
29704                         y += th - h;
29705                         x += tw - w;
29706                        break;
29707
29708                 }
29709             }
29710             if (pos == 'hdrag') {
29711                 w = ow;
29712             }
29713             this.proxy.setBounds(x, y, w, h);
29714             if(this.dynamic){
29715                 this.resizeElement();
29716             }
29717             }catch(e){}
29718         }
29719         this.fireEvent("resizing", this, x, y, w, h, e);
29720     },
29721
29722     // private
29723     handleOver : function(){
29724         if(this.enabled){
29725             this.el.addClass("x-resizable-over");
29726         }
29727     },
29728
29729     // private
29730     handleOut : function(){
29731         if(!this.resizing){
29732             this.el.removeClass("x-resizable-over");
29733         }
29734     },
29735
29736     /**
29737      * Returns the element this component is bound to.
29738      * @return {Roo.Element}
29739      */
29740     getEl : function(){
29741         return this.el;
29742     },
29743
29744     /**
29745      * Returns the resizeChild element (or null).
29746      * @return {Roo.Element}
29747      */
29748     getResizeChild : function(){
29749         return this.resizeChild;
29750     },
29751     groupHandler : function()
29752     {
29753         
29754     },
29755     /**
29756      * Destroys this resizable. If the element was wrapped and
29757      * removeEl is not true then the element remains.
29758      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29759      */
29760     destroy : function(removeEl){
29761         this.proxy.remove();
29762         if(this.overlay){
29763             this.overlay.removeAllListeners();
29764             this.overlay.remove();
29765         }
29766         var ps = Roo.Resizable.positions;
29767         for(var k in ps){
29768             if(typeof ps[k] != "function" && this[ps[k]]){
29769                 var h = this[ps[k]];
29770                 h.el.removeAllListeners();
29771                 h.el.remove();
29772             }
29773         }
29774         if(removeEl){
29775             this.el.update("");
29776             this.el.remove();
29777         }
29778     }
29779 });
29780
29781 // private
29782 // hash to map config positions to true positions
29783 Roo.Resizable.positions = {
29784     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29785     hd: "hdrag"
29786 };
29787
29788 // private
29789 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29790     if(!this.tpl){
29791         // only initialize the template if resizable is used
29792         var tpl = Roo.DomHelper.createTemplate(
29793             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29794         );
29795         tpl.compile();
29796         Roo.Resizable.Handle.prototype.tpl = tpl;
29797     }
29798     this.position = pos;
29799     this.rz = rz;
29800     // show north drag fro topdra
29801     var handlepos = pos == 'hdrag' ? 'north' : pos;
29802     
29803     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29804     if (pos == 'hdrag') {
29805         this.el.setStyle('cursor', 'pointer');
29806     }
29807     this.el.unselectable();
29808     if(transparent){
29809         this.el.setOpacity(0);
29810     }
29811     this.el.on("mousedown", this.onMouseDown, this);
29812     if(!disableTrackOver){
29813         this.el.on("mouseover", this.onMouseOver, this);
29814         this.el.on("mouseout", this.onMouseOut, this);
29815     }
29816 };
29817
29818 // private
29819 Roo.Resizable.Handle.prototype = {
29820     afterResize : function(rz){
29821         Roo.log('after?');
29822         // do nothing
29823     },
29824     // private
29825     onMouseDown : function(e){
29826         this.rz.onMouseDown(this, e);
29827     },
29828     // private
29829     onMouseOver : function(e){
29830         this.rz.handleOver(this, e);
29831     },
29832     // private
29833     onMouseOut : function(e){
29834         this.rz.handleOut(this, e);
29835     }
29836 };/*
29837  * Based on:
29838  * Ext JS Library 1.1.1
29839  * Copyright(c) 2006-2007, Ext JS, LLC.
29840  *
29841  * Originally Released Under LGPL - original licence link has changed is not relivant.
29842  *
29843  * Fork - LGPL
29844  * <script type="text/javascript">
29845  */
29846
29847 /**
29848  * @class Roo.Editor
29849  * @extends Roo.Component
29850  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29851  * @constructor
29852  * Create a new Editor
29853  * @param {Roo.form.Field} field The Field object (or descendant)
29854  * @param {Object} config The config object
29855  */
29856 Roo.Editor = function(field, config){
29857     Roo.Editor.superclass.constructor.call(this, config);
29858     this.field = field;
29859     this.addEvents({
29860         /**
29861              * @event beforestartedit
29862              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29863              * false from the handler of this event.
29864              * @param {Editor} this
29865              * @param {Roo.Element} boundEl The underlying element bound to this editor
29866              * @param {Mixed} value The field value being set
29867              */
29868         "beforestartedit" : true,
29869         /**
29870              * @event startedit
29871              * Fires when this editor is displayed
29872              * @param {Roo.Element} boundEl The underlying element bound to this editor
29873              * @param {Mixed} value The starting field value
29874              */
29875         "startedit" : true,
29876         /**
29877              * @event beforecomplete
29878              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29879              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29880              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29881              * event will not fire since no edit actually occurred.
29882              * @param {Editor} this
29883              * @param {Mixed} value The current field value
29884              * @param {Mixed} startValue The original field value
29885              */
29886         "beforecomplete" : true,
29887         /**
29888              * @event complete
29889              * Fires after editing is complete and any changed value has been written to the underlying field.
29890              * @param {Editor} this
29891              * @param {Mixed} value The current field value
29892              * @param {Mixed} startValue The original field value
29893              */
29894         "complete" : true,
29895         /**
29896          * @event specialkey
29897          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29898          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29899          * @param {Roo.form.Field} this
29900          * @param {Roo.EventObject} e The event object
29901          */
29902         "specialkey" : true
29903     });
29904 };
29905
29906 Roo.extend(Roo.Editor, Roo.Component, {
29907     /**
29908      * @cfg {Boolean/String} autosize
29909      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29910      * or "height" to adopt the height only (defaults to false)
29911      */
29912     /**
29913      * @cfg {Boolean} revertInvalid
29914      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29915      * validation fails (defaults to true)
29916      */
29917     /**
29918      * @cfg {Boolean} ignoreNoChange
29919      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29920      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29921      * will never be ignored.
29922      */
29923     /**
29924      * @cfg {Boolean} hideEl
29925      * False to keep the bound element visible while the editor is displayed (defaults to true)
29926      */
29927     /**
29928      * @cfg {Mixed} value
29929      * The data value of the underlying field (defaults to "")
29930      */
29931     value : "",
29932     /**
29933      * @cfg {String} alignment
29934      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29935      */
29936     alignment: "c-c?",
29937     /**
29938      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29939      * for bottom-right shadow (defaults to "frame")
29940      */
29941     shadow : "frame",
29942     /**
29943      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29944      */
29945     constrain : false,
29946     /**
29947      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29948      */
29949     completeOnEnter : false,
29950     /**
29951      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29952      */
29953     cancelOnEsc : false,
29954     /**
29955      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29956      */
29957     updateEl : false,
29958
29959     // private
29960     onRender : function(ct, position){
29961         this.el = new Roo.Layer({
29962             shadow: this.shadow,
29963             cls: "x-editor",
29964             parentEl : ct,
29965             shim : this.shim,
29966             shadowOffset:4,
29967             id: this.id,
29968             constrain: this.constrain
29969         });
29970         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29971         if(this.field.msgTarget != 'title'){
29972             this.field.msgTarget = 'qtip';
29973         }
29974         this.field.render(this.el);
29975         if(Roo.isGecko){
29976             this.field.el.dom.setAttribute('autocomplete', 'off');
29977         }
29978         this.field.on("specialkey", this.onSpecialKey, this);
29979         if(this.swallowKeys){
29980             this.field.el.swallowEvent(['keydown','keypress']);
29981         }
29982         this.field.show();
29983         this.field.on("blur", this.onBlur, this);
29984         if(this.field.grow){
29985             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29986         }
29987     },
29988
29989     onSpecialKey : function(field, e)
29990     {
29991         //Roo.log('editor onSpecialKey');
29992         if(this.completeOnEnter && e.getKey() == e.ENTER){
29993             e.stopEvent();
29994             this.completeEdit();
29995             return;
29996         }
29997         // do not fire special key otherwise it might hide close the editor...
29998         if(e.getKey() == e.ENTER){    
29999             return;
30000         }
30001         if(this.cancelOnEsc && e.getKey() == e.ESC){
30002             this.cancelEdit();
30003             return;
30004         } 
30005         this.fireEvent('specialkey', field, e);
30006     
30007     },
30008
30009     /**
30010      * Starts the editing process and shows the editor.
30011      * @param {String/HTMLElement/Element} el The element to edit
30012      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30013       * to the innerHTML of el.
30014      */
30015     startEdit : function(el, value){
30016         if(this.editing){
30017             this.completeEdit();
30018         }
30019         this.boundEl = Roo.get(el);
30020         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30021         if(!this.rendered){
30022             this.render(this.parentEl || document.body);
30023         }
30024         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30025             return;
30026         }
30027         this.startValue = v;
30028         this.field.setValue(v);
30029         if(this.autoSize){
30030             var sz = this.boundEl.getSize();
30031             switch(this.autoSize){
30032                 case "width":
30033                 this.setSize(sz.width,  "");
30034                 break;
30035                 case "height":
30036                 this.setSize("",  sz.height);
30037                 break;
30038                 default:
30039                 this.setSize(sz.width,  sz.height);
30040             }
30041         }
30042         this.el.alignTo(this.boundEl, this.alignment);
30043         this.editing = true;
30044         if(Roo.QuickTips){
30045             Roo.QuickTips.disable();
30046         }
30047         this.show();
30048     },
30049
30050     /**
30051      * Sets the height and width of this editor.
30052      * @param {Number} width The new width
30053      * @param {Number} height The new height
30054      */
30055     setSize : function(w, h){
30056         this.field.setSize(w, h);
30057         if(this.el){
30058             this.el.sync();
30059         }
30060     },
30061
30062     /**
30063      * Realigns the editor to the bound field based on the current alignment config value.
30064      */
30065     realign : function(){
30066         this.el.alignTo(this.boundEl, this.alignment);
30067     },
30068
30069     /**
30070      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30071      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30072      */
30073     completeEdit : function(remainVisible){
30074         if(!this.editing){
30075             return;
30076         }
30077         var v = this.getValue();
30078         if(this.revertInvalid !== false && !this.field.isValid()){
30079             v = this.startValue;
30080             this.cancelEdit(true);
30081         }
30082         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30083             this.editing = false;
30084             this.hide();
30085             return;
30086         }
30087         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30088             this.editing = false;
30089             if(this.updateEl && this.boundEl){
30090                 this.boundEl.update(v);
30091             }
30092             if(remainVisible !== true){
30093                 this.hide();
30094             }
30095             this.fireEvent("complete", this, v, this.startValue);
30096         }
30097     },
30098
30099     // private
30100     onShow : function(){
30101         this.el.show();
30102         if(this.hideEl !== false){
30103             this.boundEl.hide();
30104         }
30105         this.field.show();
30106         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30107             this.fixIEFocus = true;
30108             this.deferredFocus.defer(50, this);
30109         }else{
30110             this.field.focus();
30111         }
30112         this.fireEvent("startedit", this.boundEl, this.startValue);
30113     },
30114
30115     deferredFocus : function(){
30116         if(this.editing){
30117             this.field.focus();
30118         }
30119     },
30120
30121     /**
30122      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30123      * reverted to the original starting value.
30124      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30125      * cancel (defaults to false)
30126      */
30127     cancelEdit : function(remainVisible){
30128         if(this.editing){
30129             this.setValue(this.startValue);
30130             if(remainVisible !== true){
30131                 this.hide();
30132             }
30133         }
30134     },
30135
30136     // private
30137     onBlur : function(){
30138         if(this.allowBlur !== true && this.editing){
30139             this.completeEdit();
30140         }
30141     },
30142
30143     // private
30144     onHide : function(){
30145         if(this.editing){
30146             this.completeEdit();
30147             return;
30148         }
30149         this.field.blur();
30150         if(this.field.collapse){
30151             this.field.collapse();
30152         }
30153         this.el.hide();
30154         if(this.hideEl !== false){
30155             this.boundEl.show();
30156         }
30157         if(Roo.QuickTips){
30158             Roo.QuickTips.enable();
30159         }
30160     },
30161
30162     /**
30163      * Sets the data value of the editor
30164      * @param {Mixed} value Any valid value supported by the underlying field
30165      */
30166     setValue : function(v){
30167         this.field.setValue(v);
30168     },
30169
30170     /**
30171      * Gets the data value of the editor
30172      * @return {Mixed} The data value
30173      */
30174     getValue : function(){
30175         return this.field.getValue();
30176     }
30177 });/*
30178  * Based on:
30179  * Ext JS Library 1.1.1
30180  * Copyright(c) 2006-2007, Ext JS, LLC.
30181  *
30182  * Originally Released Under LGPL - original licence link has changed is not relivant.
30183  *
30184  * Fork - LGPL
30185  * <script type="text/javascript">
30186  */
30187  
30188 /**
30189  * @class Roo.BasicDialog
30190  * @extends Roo.util.Observable
30191  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30192  * <pre><code>
30193 var dlg = new Roo.BasicDialog("my-dlg", {
30194     height: 200,
30195     width: 300,
30196     minHeight: 100,
30197     minWidth: 150,
30198     modal: true,
30199     proxyDrag: true,
30200     shadow: true
30201 });
30202 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30203 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30204 dlg.addButton('Cancel', dlg.hide, dlg);
30205 dlg.show();
30206 </code></pre>
30207   <b>A Dialog should always be a direct child of the body element.</b>
30208  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30209  * @cfg {String} title Default text to display in the title bar (defaults to null)
30210  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30211  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30212  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30213  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30214  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30215  * (defaults to null with no animation)
30216  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30217  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30218  * property for valid values (defaults to 'all')
30219  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30220  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30221  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30222  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30223  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30224  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30225  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30226  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30227  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30228  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30229  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30230  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30231  * draggable = true (defaults to false)
30232  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30233  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30234  * shadow (defaults to false)
30235  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30236  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30237  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30238  * @cfg {Array} buttons Array of buttons
30239  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30240  * @constructor
30241  * Create a new BasicDialog.
30242  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30243  * @param {Object} config Configuration options
30244  */
30245 Roo.BasicDialog = function(el, config){
30246     this.el = Roo.get(el);
30247     var dh = Roo.DomHelper;
30248     if(!this.el && config && config.autoCreate){
30249         if(typeof config.autoCreate == "object"){
30250             if(!config.autoCreate.id){
30251                 config.autoCreate.id = el;
30252             }
30253             this.el = dh.append(document.body,
30254                         config.autoCreate, true);
30255         }else{
30256             this.el = dh.append(document.body,
30257                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30258         }
30259     }
30260     el = this.el;
30261     el.setDisplayed(true);
30262     el.hide = this.hideAction;
30263     this.id = el.id;
30264     el.addClass("x-dlg");
30265
30266     Roo.apply(this, config);
30267
30268     this.proxy = el.createProxy("x-dlg-proxy");
30269     this.proxy.hide = this.hideAction;
30270     this.proxy.setOpacity(.5);
30271     this.proxy.hide();
30272
30273     if(config.width){
30274         el.setWidth(config.width);
30275     }
30276     if(config.height){
30277         el.setHeight(config.height);
30278     }
30279     this.size = el.getSize();
30280     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30281         this.xy = [config.x,config.y];
30282     }else{
30283         this.xy = el.getCenterXY(true);
30284     }
30285     /** The header element @type Roo.Element */
30286     this.header = el.child("> .x-dlg-hd");
30287     /** The body element @type Roo.Element */
30288     this.body = el.child("> .x-dlg-bd");
30289     /** The footer element @type Roo.Element */
30290     this.footer = el.child("> .x-dlg-ft");
30291
30292     if(!this.header){
30293         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30294     }
30295     if(!this.body){
30296         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30297     }
30298
30299     this.header.unselectable();
30300     if(this.title){
30301         this.header.update(this.title);
30302     }
30303     // this element allows the dialog to be focused for keyboard event
30304     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30305     this.focusEl.swallowEvent("click", true);
30306
30307     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30308
30309     // wrap the body and footer for special rendering
30310     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30311     if(this.footer){
30312         this.bwrap.dom.appendChild(this.footer.dom);
30313     }
30314
30315     this.bg = this.el.createChild({
30316         tag: "div", cls:"x-dlg-bg",
30317         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30318     });
30319     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30320
30321
30322     if(this.autoScroll !== false && !this.autoTabs){
30323         this.body.setStyle("overflow", "auto");
30324     }
30325
30326     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30327
30328     if(this.closable !== false){
30329         this.el.addClass("x-dlg-closable");
30330         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30331         this.close.on("click", this.closeClick, this);
30332         this.close.addClassOnOver("x-dlg-close-over");
30333     }
30334     if(this.collapsible !== false){
30335         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30336         this.collapseBtn.on("click", this.collapseClick, this);
30337         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30338         this.header.on("dblclick", this.collapseClick, this);
30339     }
30340     if(this.resizable !== false){
30341         this.el.addClass("x-dlg-resizable");
30342         this.resizer = new Roo.Resizable(el, {
30343             minWidth: this.minWidth || 80,
30344             minHeight:this.minHeight || 80,
30345             handles: this.resizeHandles || "all",
30346             pinned: true
30347         });
30348         this.resizer.on("beforeresize", this.beforeResize, this);
30349         this.resizer.on("resize", this.onResize, this);
30350     }
30351     if(this.draggable !== false){
30352         el.addClass("x-dlg-draggable");
30353         if (!this.proxyDrag) {
30354             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30355         }
30356         else {
30357             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30358         }
30359         dd.setHandleElId(this.header.id);
30360         dd.endDrag = this.endMove.createDelegate(this);
30361         dd.startDrag = this.startMove.createDelegate(this);
30362         dd.onDrag = this.onDrag.createDelegate(this);
30363         dd.scroll = false;
30364         this.dd = dd;
30365     }
30366     if(this.modal){
30367         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30368         this.mask.enableDisplayMode("block");
30369         this.mask.hide();
30370         this.el.addClass("x-dlg-modal");
30371     }
30372     if(this.shadow){
30373         this.shadow = new Roo.Shadow({
30374             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30375             offset : this.shadowOffset
30376         });
30377     }else{
30378         this.shadowOffset = 0;
30379     }
30380     if(Roo.useShims && this.shim !== false){
30381         this.shim = this.el.createShim();
30382         this.shim.hide = this.hideAction;
30383         this.shim.hide();
30384     }else{
30385         this.shim = false;
30386     }
30387     if(this.autoTabs){
30388         this.initTabs();
30389     }
30390     if (this.buttons) { 
30391         var bts= this.buttons;
30392         this.buttons = [];
30393         Roo.each(bts, function(b) {
30394             this.addButton(b);
30395         }, this);
30396     }
30397     
30398     
30399     this.addEvents({
30400         /**
30401          * @event keydown
30402          * Fires when a key is pressed
30403          * @param {Roo.BasicDialog} this
30404          * @param {Roo.EventObject} e
30405          */
30406         "keydown" : true,
30407         /**
30408          * @event move
30409          * Fires when this dialog is moved by the user.
30410          * @param {Roo.BasicDialog} this
30411          * @param {Number} x The new page X
30412          * @param {Number} y The new page Y
30413          */
30414         "move" : true,
30415         /**
30416          * @event resize
30417          * Fires when this dialog is resized by the user.
30418          * @param {Roo.BasicDialog} this
30419          * @param {Number} width The new width
30420          * @param {Number} height The new height
30421          */
30422         "resize" : true,
30423         /**
30424          * @event beforehide
30425          * Fires before this dialog is hidden.
30426          * @param {Roo.BasicDialog} this
30427          */
30428         "beforehide" : true,
30429         /**
30430          * @event hide
30431          * Fires when this dialog is hidden.
30432          * @param {Roo.BasicDialog} this
30433          */
30434         "hide" : true,
30435         /**
30436          * @event beforeshow
30437          * Fires before this dialog is shown.
30438          * @param {Roo.BasicDialog} this
30439          */
30440         "beforeshow" : true,
30441         /**
30442          * @event show
30443          * Fires when this dialog is shown.
30444          * @param {Roo.BasicDialog} this
30445          */
30446         "show" : true
30447     });
30448     el.on("keydown", this.onKeyDown, this);
30449     el.on("mousedown", this.toFront, this);
30450     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30451     this.el.hide();
30452     Roo.DialogManager.register(this);
30453     Roo.BasicDialog.superclass.constructor.call(this);
30454 };
30455
30456 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30457     shadowOffset: Roo.isIE ? 6 : 5,
30458     minHeight: 80,
30459     minWidth: 200,
30460     minButtonWidth: 75,
30461     defaultButton: null,
30462     buttonAlign: "right",
30463     tabTag: 'div',
30464     firstShow: true,
30465
30466     /**
30467      * Sets the dialog title text
30468      * @param {String} text The title text to display
30469      * @return {Roo.BasicDialog} this
30470      */
30471     setTitle : function(text){
30472         this.header.update(text);
30473         return this;
30474     },
30475
30476     // private
30477     closeClick : function(){
30478         this.hide();
30479     },
30480
30481     // private
30482     collapseClick : function(){
30483         this[this.collapsed ? "expand" : "collapse"]();
30484     },
30485
30486     /**
30487      * Collapses the dialog to its minimized state (only the title bar is visible).
30488      * Equivalent to the user clicking the collapse dialog button.
30489      */
30490     collapse : function(){
30491         if(!this.collapsed){
30492             this.collapsed = true;
30493             this.el.addClass("x-dlg-collapsed");
30494             this.restoreHeight = this.el.getHeight();
30495             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30496         }
30497     },
30498
30499     /**
30500      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30501      * clicking the expand dialog button.
30502      */
30503     expand : function(){
30504         if(this.collapsed){
30505             this.collapsed = false;
30506             this.el.removeClass("x-dlg-collapsed");
30507             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30508         }
30509     },
30510
30511     /**
30512      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30513      * @return {Roo.TabPanel} The tabs component
30514      */
30515     initTabs : function(){
30516         var tabs = this.getTabs();
30517         while(tabs.getTab(0)){
30518             tabs.removeTab(0);
30519         }
30520         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30521             var dom = el.dom;
30522             tabs.addTab(Roo.id(dom), dom.title);
30523             dom.title = "";
30524         });
30525         tabs.activate(0);
30526         return tabs;
30527     },
30528
30529     // private
30530     beforeResize : function(){
30531         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30532     },
30533
30534     // private
30535     onResize : function(){
30536         this.refreshSize();
30537         this.syncBodyHeight();
30538         this.adjustAssets();
30539         this.focus();
30540         this.fireEvent("resize", this, this.size.width, this.size.height);
30541     },
30542
30543     // private
30544     onKeyDown : function(e){
30545         if(this.isVisible()){
30546             this.fireEvent("keydown", this, e);
30547         }
30548     },
30549
30550     /**
30551      * Resizes the dialog.
30552      * @param {Number} width
30553      * @param {Number} height
30554      * @return {Roo.BasicDialog} this
30555      */
30556     resizeTo : function(width, height){
30557         this.el.setSize(width, height);
30558         this.size = {width: width, height: height};
30559         this.syncBodyHeight();
30560         if(this.fixedcenter){
30561             this.center();
30562         }
30563         if(this.isVisible()){
30564             this.constrainXY();
30565             this.adjustAssets();
30566         }
30567         this.fireEvent("resize", this, width, height);
30568         return this;
30569     },
30570
30571
30572     /**
30573      * Resizes the dialog to fit the specified content size.
30574      * @param {Number} width
30575      * @param {Number} height
30576      * @return {Roo.BasicDialog} this
30577      */
30578     setContentSize : function(w, h){
30579         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30580         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30581         //if(!this.el.isBorderBox()){
30582             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30583             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30584         //}
30585         if(this.tabs){
30586             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30587             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30588         }
30589         this.resizeTo(w, h);
30590         return this;
30591     },
30592
30593     /**
30594      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30595      * executed in response to a particular key being pressed while the dialog is active.
30596      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30597      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30598      * @param {Function} fn The function to call
30599      * @param {Object} scope (optional) The scope of the function
30600      * @return {Roo.BasicDialog} this
30601      */
30602     addKeyListener : function(key, fn, scope){
30603         var keyCode, shift, ctrl, alt;
30604         if(typeof key == "object" && !(key instanceof Array)){
30605             keyCode = key["key"];
30606             shift = key["shift"];
30607             ctrl = key["ctrl"];
30608             alt = key["alt"];
30609         }else{
30610             keyCode = key;
30611         }
30612         var handler = function(dlg, e){
30613             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30614                 var k = e.getKey();
30615                 if(keyCode instanceof Array){
30616                     for(var i = 0, len = keyCode.length; i < len; i++){
30617                         if(keyCode[i] == k){
30618                           fn.call(scope || window, dlg, k, e);
30619                           return;
30620                         }
30621                     }
30622                 }else{
30623                     if(k == keyCode){
30624                         fn.call(scope || window, dlg, k, e);
30625                     }
30626                 }
30627             }
30628         };
30629         this.on("keydown", handler);
30630         return this;
30631     },
30632
30633     /**
30634      * Returns the TabPanel component (creates it if it doesn't exist).
30635      * Note: If you wish to simply check for the existence of tabs without creating them,
30636      * check for a null 'tabs' property.
30637      * @return {Roo.TabPanel} The tabs component
30638      */
30639     getTabs : function(){
30640         if(!this.tabs){
30641             this.el.addClass("x-dlg-auto-tabs");
30642             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30643             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30644         }
30645         return this.tabs;
30646     },
30647
30648     /**
30649      * Adds a button to the footer section of the dialog.
30650      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30651      * object or a valid Roo.DomHelper element config
30652      * @param {Function} handler The function called when the button is clicked
30653      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30654      * @return {Roo.Button} The new button
30655      */
30656     addButton : function(config, handler, scope){
30657         var dh = Roo.DomHelper;
30658         if(!this.footer){
30659             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30660         }
30661         if(!this.btnContainer){
30662             var tb = this.footer.createChild({
30663
30664                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30665                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30666             }, null, true);
30667             this.btnContainer = tb.firstChild.firstChild.firstChild;
30668         }
30669         var bconfig = {
30670             handler: handler,
30671             scope: scope,
30672             minWidth: this.minButtonWidth,
30673             hideParent:true
30674         };
30675         if(typeof config == "string"){
30676             bconfig.text = config;
30677         }else{
30678             if(config.tag){
30679                 bconfig.dhconfig = config;
30680             }else{
30681                 Roo.apply(bconfig, config);
30682             }
30683         }
30684         var fc = false;
30685         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30686             bconfig.position = Math.max(0, bconfig.position);
30687             fc = this.btnContainer.childNodes[bconfig.position];
30688         }
30689          
30690         var btn = new Roo.Button(
30691             fc ? 
30692                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30693                 : this.btnContainer.appendChild(document.createElement("td")),
30694             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30695             bconfig
30696         );
30697         this.syncBodyHeight();
30698         if(!this.buttons){
30699             /**
30700              * Array of all the buttons that have been added to this dialog via addButton
30701              * @type Array
30702              */
30703             this.buttons = [];
30704         }
30705         this.buttons.push(btn);
30706         return btn;
30707     },
30708
30709     /**
30710      * Sets the default button to be focused when the dialog is displayed.
30711      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30712      * @return {Roo.BasicDialog} this
30713      */
30714     setDefaultButton : function(btn){
30715         this.defaultButton = btn;
30716         return this;
30717     },
30718
30719     // private
30720     getHeaderFooterHeight : function(safe){
30721         var height = 0;
30722         if(this.header){
30723            height += this.header.getHeight();
30724         }
30725         if(this.footer){
30726            var fm = this.footer.getMargins();
30727             height += (this.footer.getHeight()+fm.top+fm.bottom);
30728         }
30729         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30730         height += this.centerBg.getPadding("tb");
30731         return height;
30732     },
30733
30734     // private
30735     syncBodyHeight : function()
30736     {
30737         var bd = this.body, // the text
30738             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30739             bw = this.bwrap;
30740         var height = this.size.height - this.getHeaderFooterHeight(false);
30741         bd.setHeight(height-bd.getMargins("tb"));
30742         var hh = this.header.getHeight();
30743         var h = this.size.height-hh;
30744         cb.setHeight(h);
30745         
30746         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30747         bw.setHeight(h-cb.getPadding("tb"));
30748         
30749         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30750         bd.setWidth(bw.getWidth(true));
30751         if(this.tabs){
30752             this.tabs.syncHeight();
30753             if(Roo.isIE){
30754                 this.tabs.el.repaint();
30755             }
30756         }
30757     },
30758
30759     /**
30760      * Restores the previous state of the dialog if Roo.state is configured.
30761      * @return {Roo.BasicDialog} this
30762      */
30763     restoreState : function(){
30764         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30765         if(box && box.width){
30766             this.xy = [box.x, box.y];
30767             this.resizeTo(box.width, box.height);
30768         }
30769         return this;
30770     },
30771
30772     // private
30773     beforeShow : function(){
30774         this.expand();
30775         if(this.fixedcenter){
30776             this.xy = this.el.getCenterXY(true);
30777         }
30778         if(this.modal){
30779             Roo.get(document.body).addClass("x-body-masked");
30780             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30781             this.mask.show();
30782         }
30783         this.constrainXY();
30784     },
30785
30786     // private
30787     animShow : function(){
30788         var b = Roo.get(this.animateTarget).getBox();
30789         this.proxy.setSize(b.width, b.height);
30790         this.proxy.setLocation(b.x, b.y);
30791         this.proxy.show();
30792         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30793                     true, .35, this.showEl.createDelegate(this));
30794     },
30795
30796     /**
30797      * Shows the dialog.
30798      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30799      * @return {Roo.BasicDialog} this
30800      */
30801     show : function(animateTarget){
30802         if (this.fireEvent("beforeshow", this) === false){
30803             return;
30804         }
30805         if(this.syncHeightBeforeShow){
30806             this.syncBodyHeight();
30807         }else if(this.firstShow){
30808             this.firstShow = false;
30809             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30810         }
30811         this.animateTarget = animateTarget || this.animateTarget;
30812         if(!this.el.isVisible()){
30813             this.beforeShow();
30814             if(this.animateTarget && Roo.get(this.animateTarget)){
30815                 this.animShow();
30816             }else{
30817                 this.showEl();
30818             }
30819         }
30820         return this;
30821     },
30822
30823     // private
30824     showEl : function(){
30825         this.proxy.hide();
30826         this.el.setXY(this.xy);
30827         this.el.show();
30828         this.adjustAssets(true);
30829         this.toFront();
30830         this.focus();
30831         // IE peekaboo bug - fix found by Dave Fenwick
30832         if(Roo.isIE){
30833             this.el.repaint();
30834         }
30835         this.fireEvent("show", this);
30836     },
30837
30838     /**
30839      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30840      * dialog itself will receive focus.
30841      */
30842     focus : function(){
30843         if(this.defaultButton){
30844             this.defaultButton.focus();
30845         }else{
30846             this.focusEl.focus();
30847         }
30848     },
30849
30850     // private
30851     constrainXY : function(){
30852         if(this.constraintoviewport !== false){
30853             if(!this.viewSize){
30854                 if(this.container){
30855                     var s = this.container.getSize();
30856                     this.viewSize = [s.width, s.height];
30857                 }else{
30858                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30859                 }
30860             }
30861             var s = Roo.get(this.container||document).getScroll();
30862
30863             var x = this.xy[0], y = this.xy[1];
30864             var w = this.size.width, h = this.size.height;
30865             var vw = this.viewSize[0], vh = this.viewSize[1];
30866             // only move it if it needs it
30867             var moved = false;
30868             // first validate right/bottom
30869             if(x + w > vw+s.left){
30870                 x = vw - w;
30871                 moved = true;
30872             }
30873             if(y + h > vh+s.top){
30874                 y = vh - h;
30875                 moved = true;
30876             }
30877             // then make sure top/left isn't negative
30878             if(x < s.left){
30879                 x = s.left;
30880                 moved = true;
30881             }
30882             if(y < s.top){
30883                 y = s.top;
30884                 moved = true;
30885             }
30886             if(moved){
30887                 // cache xy
30888                 this.xy = [x, y];
30889                 if(this.isVisible()){
30890                     this.el.setLocation(x, y);
30891                     this.adjustAssets();
30892                 }
30893             }
30894         }
30895     },
30896
30897     // private
30898     onDrag : function(){
30899         if(!this.proxyDrag){
30900             this.xy = this.el.getXY();
30901             this.adjustAssets();
30902         }
30903     },
30904
30905     // private
30906     adjustAssets : function(doShow){
30907         var x = this.xy[0], y = this.xy[1];
30908         var w = this.size.width, h = this.size.height;
30909         if(doShow === true){
30910             if(this.shadow){
30911                 this.shadow.show(this.el);
30912             }
30913             if(this.shim){
30914                 this.shim.show();
30915             }
30916         }
30917         if(this.shadow && this.shadow.isVisible()){
30918             this.shadow.show(this.el);
30919         }
30920         if(this.shim && this.shim.isVisible()){
30921             this.shim.setBounds(x, y, w, h);
30922         }
30923     },
30924
30925     // private
30926     adjustViewport : function(w, h){
30927         if(!w || !h){
30928             w = Roo.lib.Dom.getViewWidth();
30929             h = Roo.lib.Dom.getViewHeight();
30930         }
30931         // cache the size
30932         this.viewSize = [w, h];
30933         if(this.modal && this.mask.isVisible()){
30934             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30935             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30936         }
30937         if(this.isVisible()){
30938             this.constrainXY();
30939         }
30940     },
30941
30942     /**
30943      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30944      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30945      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30946      */
30947     destroy : function(removeEl){
30948         if(this.isVisible()){
30949             this.animateTarget = null;
30950             this.hide();
30951         }
30952         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30953         if(this.tabs){
30954             this.tabs.destroy(removeEl);
30955         }
30956         Roo.destroy(
30957              this.shim,
30958              this.proxy,
30959              this.resizer,
30960              this.close,
30961              this.mask
30962         );
30963         if(this.dd){
30964             this.dd.unreg();
30965         }
30966         if(this.buttons){
30967            for(var i = 0, len = this.buttons.length; i < len; i++){
30968                this.buttons[i].destroy();
30969            }
30970         }
30971         this.el.removeAllListeners();
30972         if(removeEl === true){
30973             this.el.update("");
30974             this.el.remove();
30975         }
30976         Roo.DialogManager.unregister(this);
30977     },
30978
30979     // private
30980     startMove : function(){
30981         if(this.proxyDrag){
30982             this.proxy.show();
30983         }
30984         if(this.constraintoviewport !== false){
30985             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30986         }
30987     },
30988
30989     // private
30990     endMove : function(){
30991         if(!this.proxyDrag){
30992             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30993         }else{
30994             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30995             this.proxy.hide();
30996         }
30997         this.refreshSize();
30998         this.adjustAssets();
30999         this.focus();
31000         this.fireEvent("move", this, this.xy[0], this.xy[1]);
31001     },
31002
31003     /**
31004      * Brings this dialog to the front of any other visible dialogs
31005      * @return {Roo.BasicDialog} this
31006      */
31007     toFront : function(){
31008         Roo.DialogManager.bringToFront(this);
31009         return this;
31010     },
31011
31012     /**
31013      * Sends this dialog to the back (under) of any other visible dialogs
31014      * @return {Roo.BasicDialog} this
31015      */
31016     toBack : function(){
31017         Roo.DialogManager.sendToBack(this);
31018         return this;
31019     },
31020
31021     /**
31022      * Centers this dialog in the viewport
31023      * @return {Roo.BasicDialog} this
31024      */
31025     center : function(){
31026         var xy = this.el.getCenterXY(true);
31027         this.moveTo(xy[0], xy[1]);
31028         return this;
31029     },
31030
31031     /**
31032      * Moves the dialog's top-left corner to the specified point
31033      * @param {Number} x
31034      * @param {Number} y
31035      * @return {Roo.BasicDialog} this
31036      */
31037     moveTo : function(x, y){
31038         this.xy = [x,y];
31039         if(this.isVisible()){
31040             this.el.setXY(this.xy);
31041             this.adjustAssets();
31042         }
31043         return this;
31044     },
31045
31046     /**
31047      * Aligns the dialog to the specified element
31048      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31049      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31050      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31051      * @return {Roo.BasicDialog} this
31052      */
31053     alignTo : function(element, position, offsets){
31054         this.xy = this.el.getAlignToXY(element, position, offsets);
31055         if(this.isVisible()){
31056             this.el.setXY(this.xy);
31057             this.adjustAssets();
31058         }
31059         return this;
31060     },
31061
31062     /**
31063      * Anchors an element to another element and realigns it when the window is resized.
31064      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31065      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31066      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31067      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31068      * is a number, it is used as the buffer delay (defaults to 50ms).
31069      * @return {Roo.BasicDialog} this
31070      */
31071     anchorTo : function(el, alignment, offsets, monitorScroll){
31072         var action = function(){
31073             this.alignTo(el, alignment, offsets);
31074         };
31075         Roo.EventManager.onWindowResize(action, this);
31076         var tm = typeof monitorScroll;
31077         if(tm != 'undefined'){
31078             Roo.EventManager.on(window, 'scroll', action, this,
31079                 {buffer: tm == 'number' ? monitorScroll : 50});
31080         }
31081         action.call(this);
31082         return this;
31083     },
31084
31085     /**
31086      * Returns true if the dialog is visible
31087      * @return {Boolean}
31088      */
31089     isVisible : function(){
31090         return this.el.isVisible();
31091     },
31092
31093     // private
31094     animHide : function(callback){
31095         var b = Roo.get(this.animateTarget).getBox();
31096         this.proxy.show();
31097         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31098         this.el.hide();
31099         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31100                     this.hideEl.createDelegate(this, [callback]));
31101     },
31102
31103     /**
31104      * Hides the dialog.
31105      * @param {Function} callback (optional) Function to call when the dialog is hidden
31106      * @return {Roo.BasicDialog} this
31107      */
31108     hide : function(callback){
31109         if (this.fireEvent("beforehide", this) === false){
31110             return;
31111         }
31112         if(this.shadow){
31113             this.shadow.hide();
31114         }
31115         if(this.shim) {
31116           this.shim.hide();
31117         }
31118         // sometimes animateTarget seems to get set.. causing problems...
31119         // this just double checks..
31120         if(this.animateTarget && Roo.get(this.animateTarget)) {
31121            this.animHide(callback);
31122         }else{
31123             this.el.hide();
31124             this.hideEl(callback);
31125         }
31126         return this;
31127     },
31128
31129     // private
31130     hideEl : function(callback){
31131         this.proxy.hide();
31132         if(this.modal){
31133             this.mask.hide();
31134             Roo.get(document.body).removeClass("x-body-masked");
31135         }
31136         this.fireEvent("hide", this);
31137         if(typeof callback == "function"){
31138             callback();
31139         }
31140     },
31141
31142     // private
31143     hideAction : function(){
31144         this.setLeft("-10000px");
31145         this.setTop("-10000px");
31146         this.setStyle("visibility", "hidden");
31147     },
31148
31149     // private
31150     refreshSize : function(){
31151         this.size = this.el.getSize();
31152         this.xy = this.el.getXY();
31153         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31154     },
31155
31156     // private
31157     // z-index is managed by the DialogManager and may be overwritten at any time
31158     setZIndex : function(index){
31159         if(this.modal){
31160             this.mask.setStyle("z-index", index);
31161         }
31162         if(this.shim){
31163             this.shim.setStyle("z-index", ++index);
31164         }
31165         if(this.shadow){
31166             this.shadow.setZIndex(++index);
31167         }
31168         this.el.setStyle("z-index", ++index);
31169         if(this.proxy){
31170             this.proxy.setStyle("z-index", ++index);
31171         }
31172         if(this.resizer){
31173             this.resizer.proxy.setStyle("z-index", ++index);
31174         }
31175
31176         this.lastZIndex = index;
31177     },
31178
31179     /**
31180      * Returns the element for this dialog
31181      * @return {Roo.Element} The underlying dialog Element
31182      */
31183     getEl : function(){
31184         return this.el;
31185     }
31186 });
31187
31188 /**
31189  * @class Roo.DialogManager
31190  * Provides global access to BasicDialogs that have been created and
31191  * support for z-indexing (layering) multiple open dialogs.
31192  */
31193 Roo.DialogManager = function(){
31194     var list = {};
31195     var accessList = [];
31196     var front = null;
31197
31198     // private
31199     var sortDialogs = function(d1, d2){
31200         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31201     };
31202
31203     // private
31204     var orderDialogs = function(){
31205         accessList.sort(sortDialogs);
31206         var seed = Roo.DialogManager.zseed;
31207         for(var i = 0, len = accessList.length; i < len; i++){
31208             var dlg = accessList[i];
31209             if(dlg){
31210                 dlg.setZIndex(seed + (i*10));
31211             }
31212         }
31213     };
31214
31215     return {
31216         /**
31217          * The starting z-index for BasicDialogs (defaults to 9000)
31218          * @type Number The z-index value
31219          */
31220         zseed : 9000,
31221
31222         // private
31223         register : function(dlg){
31224             list[dlg.id] = dlg;
31225             accessList.push(dlg);
31226         },
31227
31228         // private
31229         unregister : function(dlg){
31230             delete list[dlg.id];
31231             var i=0;
31232             var len=0;
31233             if(!accessList.indexOf){
31234                 for(  i = 0, len = accessList.length; i < len; i++){
31235                     if(accessList[i] == dlg){
31236                         accessList.splice(i, 1);
31237                         return;
31238                     }
31239                 }
31240             }else{
31241                  i = accessList.indexOf(dlg);
31242                 if(i != -1){
31243                     accessList.splice(i, 1);
31244                 }
31245             }
31246         },
31247
31248         /**
31249          * Gets a registered dialog by id
31250          * @param {String/Object} id The id of the dialog or a dialog
31251          * @return {Roo.BasicDialog} this
31252          */
31253         get : function(id){
31254             return typeof id == "object" ? id : list[id];
31255         },
31256
31257         /**
31258          * Brings the specified dialog to the front
31259          * @param {String/Object} dlg The id of the dialog or a dialog
31260          * @return {Roo.BasicDialog} this
31261          */
31262         bringToFront : function(dlg){
31263             dlg = this.get(dlg);
31264             if(dlg != front){
31265                 front = dlg;
31266                 dlg._lastAccess = new Date().getTime();
31267                 orderDialogs();
31268             }
31269             return dlg;
31270         },
31271
31272         /**
31273          * Sends the specified dialog to the back
31274          * @param {String/Object} dlg The id of the dialog or a dialog
31275          * @return {Roo.BasicDialog} this
31276          */
31277         sendToBack : function(dlg){
31278             dlg = this.get(dlg);
31279             dlg._lastAccess = -(new Date().getTime());
31280             orderDialogs();
31281             return dlg;
31282         },
31283
31284         /**
31285          * Hides all dialogs
31286          */
31287         hideAll : function(){
31288             for(var id in list){
31289                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31290                     list[id].hide();
31291                 }
31292             }
31293         }
31294     };
31295 }();
31296
31297 /**
31298  * @class Roo.LayoutDialog
31299  * @extends Roo.BasicDialog
31300  * Dialog which provides adjustments for working with a layout in a Dialog.
31301  * Add your necessary layout config options to the dialog's config.<br>
31302  * Example usage (including a nested layout):
31303  * <pre><code>
31304 if(!dialog){
31305     dialog = new Roo.LayoutDialog("download-dlg", {
31306         modal: true,
31307         width:600,
31308         height:450,
31309         shadow:true,
31310         minWidth:500,
31311         minHeight:350,
31312         autoTabs:true,
31313         proxyDrag:true,
31314         // layout config merges with the dialog config
31315         center:{
31316             tabPosition: "top",
31317             alwaysShowTabs: true
31318         }
31319     });
31320     dialog.addKeyListener(27, dialog.hide, dialog);
31321     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31322     dialog.addButton("Build It!", this.getDownload, this);
31323
31324     // we can even add nested layouts
31325     var innerLayout = new Roo.BorderLayout("dl-inner", {
31326         east: {
31327             initialSize: 200,
31328             autoScroll:true,
31329             split:true
31330         },
31331         center: {
31332             autoScroll:true
31333         }
31334     });
31335     innerLayout.beginUpdate();
31336     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31337     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31338     innerLayout.endUpdate(true);
31339
31340     var layout = dialog.getLayout();
31341     layout.beginUpdate();
31342     layout.add("center", new Roo.ContentPanel("standard-panel",
31343                         {title: "Download the Source", fitToFrame:true}));
31344     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31345                {title: "Build your own roo.js"}));
31346     layout.getRegion("center").showPanel(sp);
31347     layout.endUpdate();
31348 }
31349 </code></pre>
31350     * @constructor
31351     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31352     * @param {Object} config configuration options
31353   */
31354 Roo.LayoutDialog = function(el, cfg){
31355     
31356     var config=  cfg;
31357     if (typeof(cfg) == 'undefined') {
31358         config = Roo.apply({}, el);
31359         // not sure why we use documentElement here.. - it should always be body.
31360         // IE7 borks horribly if we use documentElement.
31361         // webkit also does not like documentElement - it creates a body element...
31362         el = Roo.get( document.body || document.documentElement ).createChild();
31363         //config.autoCreate = true;
31364     }
31365     
31366     
31367     config.autoTabs = false;
31368     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31369     this.body.setStyle({overflow:"hidden", position:"relative"});
31370     this.layout = new Roo.BorderLayout(this.body.dom, config);
31371     this.layout.monitorWindowResize = false;
31372     this.el.addClass("x-dlg-auto-layout");
31373     // fix case when center region overwrites center function
31374     this.center = Roo.BasicDialog.prototype.center;
31375     this.on("show", this.layout.layout, this.layout, true);
31376     if (config.items) {
31377         var xitems = config.items;
31378         delete config.items;
31379         Roo.each(xitems, this.addxtype, this);
31380     }
31381     
31382     
31383 };
31384 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31385     /**
31386      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31387      * @deprecated
31388      */
31389     endUpdate : function(){
31390         this.layout.endUpdate();
31391     },
31392
31393     /**
31394      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31395      *  @deprecated
31396      */
31397     beginUpdate : function(){
31398         this.layout.beginUpdate();
31399     },
31400
31401     /**
31402      * Get the BorderLayout for this dialog
31403      * @return {Roo.BorderLayout}
31404      */
31405     getLayout : function(){
31406         return this.layout;
31407     },
31408
31409     showEl : function(){
31410         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31411         if(Roo.isIE7){
31412             this.layout.layout();
31413         }
31414     },
31415
31416     // private
31417     // Use the syncHeightBeforeShow config option to control this automatically
31418     syncBodyHeight : function(){
31419         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31420         if(this.layout){this.layout.layout();}
31421     },
31422     
31423       /**
31424      * Add an xtype element (actually adds to the layout.)
31425      * @return {Object} xdata xtype object data.
31426      */
31427     
31428     addxtype : function(c) {
31429         return this.layout.addxtype(c);
31430     }
31431 });/*
31432  * Based on:
31433  * Ext JS Library 1.1.1
31434  * Copyright(c) 2006-2007, Ext JS, LLC.
31435  *
31436  * Originally Released Under LGPL - original licence link has changed is not relivant.
31437  *
31438  * Fork - LGPL
31439  * <script type="text/javascript">
31440  */
31441  
31442 /**
31443  * @class Roo.MessageBox
31444  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31445  * Example usage:
31446  *<pre><code>
31447 // Basic alert:
31448 Roo.Msg.alert('Status', 'Changes saved successfully.');
31449
31450 // Prompt for user data:
31451 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31452     if (btn == 'ok'){
31453         // process text value...
31454     }
31455 });
31456
31457 // Show a dialog using config options:
31458 Roo.Msg.show({
31459    title:'Save Changes?',
31460    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31461    buttons: Roo.Msg.YESNOCANCEL,
31462    fn: processResult,
31463    animEl: 'elId'
31464 });
31465 </code></pre>
31466  * @singleton
31467  */
31468 Roo.MessageBox = function(){
31469     var dlg, opt, mask, waitTimer;
31470     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31471     var buttons, activeTextEl, bwidth;
31472
31473     // private
31474     var handleButton = function(button){
31475         dlg.hide();
31476         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31477     };
31478
31479     // private
31480     var handleHide = function(){
31481         if(opt && opt.cls){
31482             dlg.el.removeClass(opt.cls);
31483         }
31484         if(waitTimer){
31485             Roo.TaskMgr.stop(waitTimer);
31486             waitTimer = null;
31487         }
31488     };
31489
31490     // private
31491     var updateButtons = function(b){
31492         var width = 0;
31493         if(!b){
31494             buttons["ok"].hide();
31495             buttons["cancel"].hide();
31496             buttons["yes"].hide();
31497             buttons["no"].hide();
31498             dlg.footer.dom.style.display = 'none';
31499             return width;
31500         }
31501         dlg.footer.dom.style.display = '';
31502         for(var k in buttons){
31503             if(typeof buttons[k] != "function"){
31504                 if(b[k]){
31505                     buttons[k].show();
31506                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31507                     width += buttons[k].el.getWidth()+15;
31508                 }else{
31509                     buttons[k].hide();
31510                 }
31511             }
31512         }
31513         return width;
31514     };
31515
31516     // private
31517     var handleEsc = function(d, k, e){
31518         if(opt && opt.closable !== false){
31519             dlg.hide();
31520         }
31521         if(e){
31522             e.stopEvent();
31523         }
31524     };
31525
31526     return {
31527         /**
31528          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31529          * @return {Roo.BasicDialog} The BasicDialog element
31530          */
31531         getDialog : function(){
31532            if(!dlg){
31533                 dlg = new Roo.BasicDialog("x-msg-box", {
31534                     autoCreate : true,
31535                     shadow: true,
31536                     draggable: true,
31537                     resizable:false,
31538                     constraintoviewport:false,
31539                     fixedcenter:true,
31540                     collapsible : false,
31541                     shim:true,
31542                     modal: true,
31543                     width:400, height:100,
31544                     buttonAlign:"center",
31545                     closeClick : function(){
31546                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31547                             handleButton("no");
31548                         }else{
31549                             handleButton("cancel");
31550                         }
31551                     }
31552                 });
31553                 dlg.on("hide", handleHide);
31554                 mask = dlg.mask;
31555                 dlg.addKeyListener(27, handleEsc);
31556                 buttons = {};
31557                 var bt = this.buttonText;
31558                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31559                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31560                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31561                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31562                 bodyEl = dlg.body.createChild({
31563
31564                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
31565                 });
31566                 msgEl = bodyEl.dom.firstChild;
31567                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31568                 textboxEl.enableDisplayMode();
31569                 textboxEl.addKeyListener([10,13], function(){
31570                     if(dlg.isVisible() && opt && opt.buttons){
31571                         if(opt.buttons.ok){
31572                             handleButton("ok");
31573                         }else if(opt.buttons.yes){
31574                             handleButton("yes");
31575                         }
31576                     }
31577                 });
31578                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31579                 textareaEl.enableDisplayMode();
31580                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31581                 progressEl.enableDisplayMode();
31582                 var pf = progressEl.dom.firstChild;
31583                 if (pf) {
31584                     pp = Roo.get(pf.firstChild);
31585                     pp.setHeight(pf.offsetHeight);
31586                 }
31587                 
31588             }
31589             return dlg;
31590         },
31591
31592         /**
31593          * Updates the message box body text
31594          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31595          * the XHTML-compliant non-breaking space character '&amp;#160;')
31596          * @return {Roo.MessageBox} This message box
31597          */
31598         updateText : function(text){
31599             if(!dlg.isVisible() && !opt.width){
31600                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31601             }
31602             msgEl.innerHTML = text || '&#160;';
31603       
31604             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31605             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31606             var w = Math.max(
31607                     Math.min(opt.width || cw , this.maxWidth), 
31608                     Math.max(opt.minWidth || this.minWidth, bwidth)
31609             );
31610             if(opt.prompt){
31611                 activeTextEl.setWidth(w);
31612             }
31613             if(dlg.isVisible()){
31614                 dlg.fixedcenter = false;
31615             }
31616             // to big, make it scroll. = But as usual stupid IE does not support
31617             // !important..
31618             
31619             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31620                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31621                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31622             } else {
31623                 bodyEl.dom.style.height = '';
31624                 bodyEl.dom.style.overflowY = '';
31625             }
31626             if (cw > w) {
31627                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31628             } else {
31629                 bodyEl.dom.style.overflowX = '';
31630             }
31631             
31632             dlg.setContentSize(w, bodyEl.getHeight());
31633             if(dlg.isVisible()){
31634                 dlg.fixedcenter = true;
31635             }
31636             return this;
31637         },
31638
31639         /**
31640          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31641          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31642          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31643          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31644          * @return {Roo.MessageBox} This message box
31645          */
31646         updateProgress : function(value, text){
31647             if(text){
31648                 this.updateText(text);
31649             }
31650             if (pp) { // weird bug on my firefox - for some reason this is not defined
31651                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31652             }
31653             return this;
31654         },        
31655
31656         /**
31657          * Returns true if the message box is currently displayed
31658          * @return {Boolean} True if the message box is visible, else false
31659          */
31660         isVisible : function(){
31661             return dlg && dlg.isVisible();  
31662         },
31663
31664         /**
31665          * Hides the message box if it is displayed
31666          */
31667         hide : function(){
31668             if(this.isVisible()){
31669                 dlg.hide();
31670             }  
31671         },
31672
31673         /**
31674          * Displays a new message box, or reinitializes an existing message box, based on the config options
31675          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31676          * The following config object properties are supported:
31677          * <pre>
31678 Property    Type             Description
31679 ----------  ---------------  ------------------------------------------------------------------------------------
31680 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31681                                    closes (defaults to undefined)
31682 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31683                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31684 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31685                                    progress and wait dialogs will ignore this property and always hide the
31686                                    close button as they can only be closed programmatically.
31687 cls               String           A custom CSS class to apply to the message box element
31688 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31689                                    displayed (defaults to 75)
31690 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31691                                    function will be btn (the name of the button that was clicked, if applicable,
31692                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31693                                    Progress and wait dialogs will ignore this option since they do not respond to
31694                                    user actions and can only be closed programmatically, so any required function
31695                                    should be called by the same code after it closes the dialog.
31696 icon              String           A CSS class that provides a background image to be used as an icon for
31697                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31698 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31699 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31700 modal             Boolean          False to allow user interaction with the page while the message box is
31701                                    displayed (defaults to true)
31702 msg               String           A string that will replace the existing message box body text (defaults
31703                                    to the XHTML-compliant non-breaking space character '&#160;')
31704 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31705 progress          Boolean          True to display a progress bar (defaults to false)
31706 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31707 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31708 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31709 title             String           The title text
31710 value             String           The string value to set into the active textbox element if displayed
31711 wait              Boolean          True to display a progress bar (defaults to false)
31712 width             Number           The width of the dialog in pixels
31713 </pre>
31714          *
31715          * Example usage:
31716          * <pre><code>
31717 Roo.Msg.show({
31718    title: 'Address',
31719    msg: 'Please enter your address:',
31720    width: 300,
31721    buttons: Roo.MessageBox.OKCANCEL,
31722    multiline: true,
31723    fn: saveAddress,
31724    animEl: 'addAddressBtn'
31725 });
31726 </code></pre>
31727          * @param {Object} config Configuration options
31728          * @return {Roo.MessageBox} This message box
31729          */
31730         show : function(options)
31731         {
31732             
31733             // this causes nightmares if you show one dialog after another
31734             // especially on callbacks..
31735              
31736             if(this.isVisible()){
31737                 
31738                 this.hide();
31739                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31740                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31741                 Roo.log("New Dialog Message:" +  options.msg )
31742                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31743                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31744                 
31745             }
31746             var d = this.getDialog();
31747             opt = options;
31748             d.setTitle(opt.title || "&#160;");
31749             d.close.setDisplayed(opt.closable !== false);
31750             activeTextEl = textboxEl;
31751             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31752             if(opt.prompt){
31753                 if(opt.multiline){
31754                     textboxEl.hide();
31755                     textareaEl.show();
31756                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31757                         opt.multiline : this.defaultTextHeight);
31758                     activeTextEl = textareaEl;
31759                 }else{
31760                     textboxEl.show();
31761                     textareaEl.hide();
31762                 }
31763             }else{
31764                 textboxEl.hide();
31765                 textareaEl.hide();
31766             }
31767             progressEl.setDisplayed(opt.progress === true);
31768             this.updateProgress(0);
31769             activeTextEl.dom.value = opt.value || "";
31770             if(opt.prompt){
31771                 dlg.setDefaultButton(activeTextEl);
31772             }else{
31773                 var bs = opt.buttons;
31774                 var db = null;
31775                 if(bs && bs.ok){
31776                     db = buttons["ok"];
31777                 }else if(bs && bs.yes){
31778                     db = buttons["yes"];
31779                 }
31780                 dlg.setDefaultButton(db);
31781             }
31782             bwidth = updateButtons(opt.buttons);
31783             this.updateText(opt.msg);
31784             if(opt.cls){
31785                 d.el.addClass(opt.cls);
31786             }
31787             d.proxyDrag = opt.proxyDrag === true;
31788             d.modal = opt.modal !== false;
31789             d.mask = opt.modal !== false ? mask : false;
31790             if(!d.isVisible()){
31791                 // force it to the end of the z-index stack so it gets a cursor in FF
31792                 document.body.appendChild(dlg.el.dom);
31793                 d.animateTarget = null;
31794                 d.show(options.animEl);
31795             }
31796             return this;
31797         },
31798
31799         /**
31800          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31801          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31802          * and closing the message box when the process is complete.
31803          * @param {String} title The title bar text
31804          * @param {String} msg The message box body text
31805          * @return {Roo.MessageBox} This message box
31806          */
31807         progress : function(title, msg){
31808             this.show({
31809                 title : title,
31810                 msg : msg,
31811                 buttons: false,
31812                 progress:true,
31813                 closable:false,
31814                 minWidth: this.minProgressWidth,
31815                 modal : true
31816             });
31817             return this;
31818         },
31819
31820         /**
31821          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31822          * If a callback function is passed it will be called after the user clicks the button, and the
31823          * id of the button that was clicked will be passed as the only parameter to the callback
31824          * (could also be the top-right close button).
31825          * @param {String} title The title bar text
31826          * @param {String} msg The message box body text
31827          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31828          * @param {Object} scope (optional) The scope of the callback function
31829          * @return {Roo.MessageBox} This message box
31830          */
31831         alert : function(title, msg, fn, scope){
31832             this.show({
31833                 title : title,
31834                 msg : msg,
31835                 buttons: this.OK,
31836                 fn: fn,
31837                 scope : scope,
31838                 modal : true
31839             });
31840             return this;
31841         },
31842
31843         /**
31844          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31845          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31846          * You are responsible for closing the message box when the process is complete.
31847          * @param {String} msg The message box body text
31848          * @param {String} title (optional) The title bar text
31849          * @return {Roo.MessageBox} This message box
31850          */
31851         wait : function(msg, title){
31852             this.show({
31853                 title : title,
31854                 msg : msg,
31855                 buttons: false,
31856                 closable:false,
31857                 progress:true,
31858                 modal:true,
31859                 width:300,
31860                 wait:true
31861             });
31862             waitTimer = Roo.TaskMgr.start({
31863                 run: function(i){
31864                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31865                 },
31866                 interval: 1000
31867             });
31868             return this;
31869         },
31870
31871         /**
31872          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31873          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31874          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31875          * @param {String} title The title bar text
31876          * @param {String} msg The message box body text
31877          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31878          * @param {Object} scope (optional) The scope of the callback function
31879          * @return {Roo.MessageBox} This message box
31880          */
31881         confirm : function(title, msg, fn, scope){
31882             this.show({
31883                 title : title,
31884                 msg : msg,
31885                 buttons: this.YESNO,
31886                 fn: fn,
31887                 scope : scope,
31888                 modal : true
31889             });
31890             return this;
31891         },
31892
31893         /**
31894          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31895          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31896          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31897          * (could also be the top-right close button) and the text that was entered will be passed as the two
31898          * parameters to the callback.
31899          * @param {String} title The title bar text
31900          * @param {String} msg The message box body text
31901          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31902          * @param {Object} scope (optional) The scope of the callback function
31903          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31904          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31905          * @return {Roo.MessageBox} This message box
31906          */
31907         prompt : function(title, msg, fn, scope, multiline){
31908             this.show({
31909                 title : title,
31910                 msg : msg,
31911                 buttons: this.OKCANCEL,
31912                 fn: fn,
31913                 minWidth:250,
31914                 scope : scope,
31915                 prompt:true,
31916                 multiline: multiline,
31917                 modal : true
31918             });
31919             return this;
31920         },
31921
31922         /**
31923          * Button config that displays a single OK button
31924          * @type Object
31925          */
31926         OK : {ok:true},
31927         /**
31928          * Button config that displays Yes and No buttons
31929          * @type Object
31930          */
31931         YESNO : {yes:true, no:true},
31932         /**
31933          * Button config that displays OK and Cancel buttons
31934          * @type Object
31935          */
31936         OKCANCEL : {ok:true, cancel:true},
31937         /**
31938          * Button config that displays Yes, No and Cancel buttons
31939          * @type Object
31940          */
31941         YESNOCANCEL : {yes:true, no:true, cancel:true},
31942
31943         /**
31944          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31945          * @type Number
31946          */
31947         defaultTextHeight : 75,
31948         /**
31949          * The maximum width in pixels of the message box (defaults to 600)
31950          * @type Number
31951          */
31952         maxWidth : 600,
31953         /**
31954          * The minimum width in pixels of the message box (defaults to 100)
31955          * @type Number
31956          */
31957         minWidth : 100,
31958         /**
31959          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31960          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31961          * @type Number
31962          */
31963         minProgressWidth : 250,
31964         /**
31965          * An object containing the default button text strings that can be overriden for localized language support.
31966          * Supported properties are: ok, cancel, yes and no.
31967          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31968          * @type Object
31969          */
31970         buttonText : {
31971             ok : "OK",
31972             cancel : "Cancel",
31973             yes : "Yes",
31974             no : "No"
31975         }
31976     };
31977 }();
31978
31979 /**
31980  * Shorthand for {@link Roo.MessageBox}
31981  */
31982 Roo.Msg = Roo.MessageBox;/*
31983  * Based on:
31984  * Ext JS Library 1.1.1
31985  * Copyright(c) 2006-2007, Ext JS, LLC.
31986  *
31987  * Originally Released Under LGPL - original licence link has changed is not relivant.
31988  *
31989  * Fork - LGPL
31990  * <script type="text/javascript">
31991  */
31992 /**
31993  * @class Roo.QuickTips
31994  * Provides attractive and customizable tooltips for any element.
31995  * @singleton
31996  */
31997 Roo.QuickTips = function(){
31998     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31999     var ce, bd, xy, dd;
32000     var visible = false, disabled = true, inited = false;
32001     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
32002     
32003     var onOver = function(e){
32004         if(disabled){
32005             return;
32006         }
32007         var t = e.getTarget();
32008         if(!t || t.nodeType !== 1 || t == document || t == document.body){
32009             return;
32010         }
32011         if(ce && t == ce.el){
32012             clearTimeout(hideProc);
32013             return;
32014         }
32015         if(t && tagEls[t.id]){
32016             tagEls[t.id].el = t;
32017             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32018             return;
32019         }
32020         var ttp, et = Roo.fly(t);
32021         var ns = cfg.namespace;
32022         if(tm.interceptTitles && t.title){
32023             ttp = t.title;
32024             t.qtip = ttp;
32025             t.removeAttribute("title");
32026             e.preventDefault();
32027         }else{
32028             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32029         }
32030         if(ttp){
32031             showProc = show.defer(tm.showDelay, tm, [{
32032                 el: t, 
32033                 text: ttp, 
32034                 width: et.getAttributeNS(ns, cfg.width),
32035                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32036                 title: et.getAttributeNS(ns, cfg.title),
32037                     cls: et.getAttributeNS(ns, cfg.cls)
32038             }]);
32039         }
32040     };
32041     
32042     var onOut = function(e){
32043         clearTimeout(showProc);
32044         var t = e.getTarget();
32045         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32046             hideProc = setTimeout(hide, tm.hideDelay);
32047         }
32048     };
32049     
32050     var onMove = function(e){
32051         if(disabled){
32052             return;
32053         }
32054         xy = e.getXY();
32055         xy[1] += 18;
32056         if(tm.trackMouse && ce){
32057             el.setXY(xy);
32058         }
32059     };
32060     
32061     var onDown = function(e){
32062         clearTimeout(showProc);
32063         clearTimeout(hideProc);
32064         if(!e.within(el)){
32065             if(tm.hideOnClick){
32066                 hide();
32067                 tm.disable();
32068                 tm.enable.defer(100, tm);
32069             }
32070         }
32071     };
32072     
32073     var getPad = function(){
32074         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32075     };
32076
32077     var show = function(o){
32078         if(disabled){
32079             return;
32080         }
32081         clearTimeout(dismissProc);
32082         ce = o;
32083         if(removeCls){ // in case manually hidden
32084             el.removeClass(removeCls);
32085             removeCls = null;
32086         }
32087         if(ce.cls){
32088             el.addClass(ce.cls);
32089             removeCls = ce.cls;
32090         }
32091         if(ce.title){
32092             tipTitle.update(ce.title);
32093             tipTitle.show();
32094         }else{
32095             tipTitle.update('');
32096             tipTitle.hide();
32097         }
32098         el.dom.style.width  = tm.maxWidth+'px';
32099         //tipBody.dom.style.width = '';
32100         tipBodyText.update(o.text);
32101         var p = getPad(), w = ce.width;
32102         if(!w){
32103             var td = tipBodyText.dom;
32104             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32105             if(aw > tm.maxWidth){
32106                 w = tm.maxWidth;
32107             }else if(aw < tm.minWidth){
32108                 w = tm.minWidth;
32109             }else{
32110                 w = aw;
32111             }
32112         }
32113         //tipBody.setWidth(w);
32114         el.setWidth(parseInt(w, 10) + p);
32115         if(ce.autoHide === false){
32116             close.setDisplayed(true);
32117             if(dd){
32118                 dd.unlock();
32119             }
32120         }else{
32121             close.setDisplayed(false);
32122             if(dd){
32123                 dd.lock();
32124             }
32125         }
32126         if(xy){
32127             el.avoidY = xy[1]-18;
32128             el.setXY(xy);
32129         }
32130         if(tm.animate){
32131             el.setOpacity(.1);
32132             el.setStyle("visibility", "visible");
32133             el.fadeIn({callback: afterShow});
32134         }else{
32135             afterShow();
32136         }
32137     };
32138     
32139     var afterShow = function(){
32140         if(ce){
32141             el.show();
32142             esc.enable();
32143             if(tm.autoDismiss && ce.autoHide !== false){
32144                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32145             }
32146         }
32147     };
32148     
32149     var hide = function(noanim){
32150         clearTimeout(dismissProc);
32151         clearTimeout(hideProc);
32152         ce = null;
32153         if(el.isVisible()){
32154             esc.disable();
32155             if(noanim !== true && tm.animate){
32156                 el.fadeOut({callback: afterHide});
32157             }else{
32158                 afterHide();
32159             } 
32160         }
32161     };
32162     
32163     var afterHide = function(){
32164         el.hide();
32165         if(removeCls){
32166             el.removeClass(removeCls);
32167             removeCls = null;
32168         }
32169     };
32170     
32171     return {
32172         /**
32173         * @cfg {Number} minWidth
32174         * The minimum width of the quick tip (defaults to 40)
32175         */
32176        minWidth : 40,
32177         /**
32178         * @cfg {Number} maxWidth
32179         * The maximum width of the quick tip (defaults to 300)
32180         */
32181        maxWidth : 300,
32182         /**
32183         * @cfg {Boolean} interceptTitles
32184         * True to automatically use the element's DOM title value if available (defaults to false)
32185         */
32186        interceptTitles : false,
32187         /**
32188         * @cfg {Boolean} trackMouse
32189         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32190         */
32191        trackMouse : false,
32192         /**
32193         * @cfg {Boolean} hideOnClick
32194         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32195         */
32196        hideOnClick : true,
32197         /**
32198         * @cfg {Number} showDelay
32199         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32200         */
32201        showDelay : 500,
32202         /**
32203         * @cfg {Number} hideDelay
32204         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32205         */
32206        hideDelay : 200,
32207         /**
32208         * @cfg {Boolean} autoHide
32209         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32210         * Used in conjunction with hideDelay.
32211         */
32212        autoHide : true,
32213         /**
32214         * @cfg {Boolean}
32215         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32216         * (defaults to true).  Used in conjunction with autoDismissDelay.
32217         */
32218        autoDismiss : true,
32219         /**
32220         * @cfg {Number}
32221         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32222         */
32223        autoDismissDelay : 5000,
32224        /**
32225         * @cfg {Boolean} animate
32226         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32227         */
32228        animate : false,
32229
32230        /**
32231         * @cfg {String} title
32232         * Title text to display (defaults to '').  This can be any valid HTML markup.
32233         */
32234         title: '',
32235        /**
32236         * @cfg {String} text
32237         * Body text to display (defaults to '').  This can be any valid HTML markup.
32238         */
32239         text : '',
32240        /**
32241         * @cfg {String} cls
32242         * A CSS class to apply to the base quick tip element (defaults to '').
32243         */
32244         cls : '',
32245        /**
32246         * @cfg {Number} width
32247         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32248         * minWidth or maxWidth.
32249         */
32250         width : null,
32251
32252     /**
32253      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32254      * or display QuickTips in a page.
32255      */
32256        init : function(){
32257           tm = Roo.QuickTips;
32258           cfg = tm.tagConfig;
32259           if(!inited){
32260               if(!Roo.isReady){ // allow calling of init() before onReady
32261                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32262                   return;
32263               }
32264               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32265               el.fxDefaults = {stopFx: true};
32266               // maximum custom styling
32267               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
32268               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
32269               tipTitle = el.child('h3');
32270               tipTitle.enableDisplayMode("block");
32271               tipBody = el.child('div.x-tip-bd');
32272               tipBodyText = el.child('div.x-tip-bd-inner');
32273               //bdLeft = el.child('div.x-tip-bd-left');
32274               //bdRight = el.child('div.x-tip-bd-right');
32275               close = el.child('div.x-tip-close');
32276               close.enableDisplayMode("block");
32277               close.on("click", hide);
32278               var d = Roo.get(document);
32279               d.on("mousedown", onDown);
32280               d.on("mouseover", onOver);
32281               d.on("mouseout", onOut);
32282               d.on("mousemove", onMove);
32283               esc = d.addKeyListener(27, hide);
32284               esc.disable();
32285               if(Roo.dd.DD){
32286                   dd = el.initDD("default", null, {
32287                       onDrag : function(){
32288                           el.sync();  
32289                       }
32290                   });
32291                   dd.setHandleElId(tipTitle.id);
32292                   dd.lock();
32293               }
32294               inited = true;
32295           }
32296           this.enable(); 
32297        },
32298
32299     /**
32300      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32301      * are supported:
32302      * <pre>
32303 Property    Type                   Description
32304 ----------  ---------------------  ------------------------------------------------------------------------
32305 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32306      * </ul>
32307      * @param {Object} config The config object
32308      */
32309        register : function(config){
32310            var cs = config instanceof Array ? config : arguments;
32311            for(var i = 0, len = cs.length; i < len; i++) {
32312                var c = cs[i];
32313                var target = c.target;
32314                if(target){
32315                    if(target instanceof Array){
32316                        for(var j = 0, jlen = target.length; j < jlen; j++){
32317                            tagEls[target[j]] = c;
32318                        }
32319                    }else{
32320                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32321                    }
32322                }
32323            }
32324        },
32325
32326     /**
32327      * Removes this quick tip from its element and destroys it.
32328      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32329      */
32330        unregister : function(el){
32331            delete tagEls[Roo.id(el)];
32332        },
32333
32334     /**
32335      * Enable this quick tip.
32336      */
32337        enable : function(){
32338            if(inited && disabled){
32339                locks.pop();
32340                if(locks.length < 1){
32341                    disabled = false;
32342                }
32343            }
32344        },
32345
32346     /**
32347      * Disable this quick tip.
32348      */
32349        disable : function(){
32350           disabled = true;
32351           clearTimeout(showProc);
32352           clearTimeout(hideProc);
32353           clearTimeout(dismissProc);
32354           if(ce){
32355               hide(true);
32356           }
32357           locks.push(1);
32358        },
32359
32360     /**
32361      * Returns true if the quick tip is enabled, else false.
32362      */
32363        isEnabled : function(){
32364             return !disabled;
32365        },
32366
32367         // private
32368        tagConfig : {
32369            namespace : "ext",
32370            attribute : "qtip",
32371            width : "width",
32372            target : "target",
32373            title : "qtitle",
32374            hide : "hide",
32375            cls : "qclass"
32376        }
32377    };
32378 }();
32379
32380 // backwards compat
32381 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32382  * Based on:
32383  * Ext JS Library 1.1.1
32384  * Copyright(c) 2006-2007, Ext JS, LLC.
32385  *
32386  * Originally Released Under LGPL - original licence link has changed is not relivant.
32387  *
32388  * Fork - LGPL
32389  * <script type="text/javascript">
32390  */
32391  
32392
32393 /**
32394  * @class Roo.tree.TreePanel
32395  * @extends Roo.data.Tree
32396
32397  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32398  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32399  * @cfg {Boolean} enableDD true to enable drag and drop
32400  * @cfg {Boolean} enableDrag true to enable just drag
32401  * @cfg {Boolean} enableDrop true to enable just drop
32402  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32403  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32404  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32405  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32406  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32407  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32408  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32409  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32410  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32411  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32412  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32413  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32414  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32415  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32416  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
32417  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
32418  * 
32419  * @constructor
32420  * @param {String/HTMLElement/Element} el The container element
32421  * @param {Object} config
32422  */
32423 Roo.tree.TreePanel = function(el, config){
32424     var root = false;
32425     var loader = false;
32426     if (config.root) {
32427         root = config.root;
32428         delete config.root;
32429     }
32430     if (config.loader) {
32431         loader = config.loader;
32432         delete config.loader;
32433     }
32434     
32435     Roo.apply(this, config);
32436     Roo.tree.TreePanel.superclass.constructor.call(this);
32437     this.el = Roo.get(el);
32438     this.el.addClass('x-tree');
32439     //console.log(root);
32440     if (root) {
32441         this.setRootNode( Roo.factory(root, Roo.tree));
32442     }
32443     if (loader) {
32444         this.loader = Roo.factory(loader, Roo.tree);
32445     }
32446    /**
32447     * Read-only. The id of the container element becomes this TreePanel's id.
32448     */
32449     this.id = this.el.id;
32450     this.addEvents({
32451         /**
32452         * @event beforeload
32453         * Fires before a node is loaded, return false to cancel
32454         * @param {Node} node The node being loaded
32455         */
32456         "beforeload" : true,
32457         /**
32458         * @event load
32459         * Fires when a node is loaded
32460         * @param {Node} node The node that was loaded
32461         */
32462         "load" : true,
32463         /**
32464         * @event textchange
32465         * Fires when the text for a node is changed
32466         * @param {Node} node The node
32467         * @param {String} text The new text
32468         * @param {String} oldText The old text
32469         */
32470         "textchange" : true,
32471         /**
32472         * @event beforeexpand
32473         * Fires before a node is expanded, return false to cancel.
32474         * @param {Node} node The node
32475         * @param {Boolean} deep
32476         * @param {Boolean} anim
32477         */
32478         "beforeexpand" : true,
32479         /**
32480         * @event beforecollapse
32481         * Fires before a node is collapsed, return false to cancel.
32482         * @param {Node} node The node
32483         * @param {Boolean} deep
32484         * @param {Boolean} anim
32485         */
32486         "beforecollapse" : true,
32487         /**
32488         * @event expand
32489         * Fires when a node is expanded
32490         * @param {Node} node The node
32491         */
32492         "expand" : true,
32493         /**
32494         * @event disabledchange
32495         * Fires when the disabled status of a node changes
32496         * @param {Node} node The node
32497         * @param {Boolean} disabled
32498         */
32499         "disabledchange" : true,
32500         /**
32501         * @event collapse
32502         * Fires when a node is collapsed
32503         * @param {Node} node The node
32504         */
32505         "collapse" : true,
32506         /**
32507         * @event beforeclick
32508         * Fires before click processing on a node. Return false to cancel the default action.
32509         * @param {Node} node The node
32510         * @param {Roo.EventObject} e The event object
32511         */
32512         "beforeclick":true,
32513         /**
32514         * @event checkchange
32515         * Fires when a node with a checkbox's checked property changes
32516         * @param {Node} this This node
32517         * @param {Boolean} checked
32518         */
32519         "checkchange":true,
32520         /**
32521         * @event click
32522         * Fires when a node is clicked
32523         * @param {Node} node The node
32524         * @param {Roo.EventObject} e The event object
32525         */
32526         "click":true,
32527         /**
32528         * @event dblclick
32529         * Fires when a node is double clicked
32530         * @param {Node} node The node
32531         * @param {Roo.EventObject} e The event object
32532         */
32533         "dblclick":true,
32534         /**
32535         * @event contextmenu
32536         * Fires when a node is right clicked
32537         * @param {Node} node The node
32538         * @param {Roo.EventObject} e The event object
32539         */
32540         "contextmenu":true,
32541         /**
32542         * @event beforechildrenrendered
32543         * Fires right before the child nodes for a node are rendered
32544         * @param {Node} node The node
32545         */
32546         "beforechildrenrendered":true,
32547         /**
32548         * @event startdrag
32549         * Fires when a node starts being dragged
32550         * @param {Roo.tree.TreePanel} this
32551         * @param {Roo.tree.TreeNode} node
32552         * @param {event} e The raw browser event
32553         */ 
32554        "startdrag" : true,
32555        /**
32556         * @event enddrag
32557         * Fires when a drag operation is complete
32558         * @param {Roo.tree.TreePanel} this
32559         * @param {Roo.tree.TreeNode} node
32560         * @param {event} e The raw browser event
32561         */
32562        "enddrag" : true,
32563        /**
32564         * @event dragdrop
32565         * Fires when a dragged node is dropped on a valid DD target
32566         * @param {Roo.tree.TreePanel} this
32567         * @param {Roo.tree.TreeNode} node
32568         * @param {DD} dd The dd it was dropped on
32569         * @param {event} e The raw browser event
32570         */
32571        "dragdrop" : true,
32572        /**
32573         * @event beforenodedrop
32574         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32575         * passed to handlers has the following properties:<br />
32576         * <ul style="padding:5px;padding-left:16px;">
32577         * <li>tree - The TreePanel</li>
32578         * <li>target - The node being targeted for the drop</li>
32579         * <li>data - The drag data from the drag source</li>
32580         * <li>point - The point of the drop - append, above or below</li>
32581         * <li>source - The drag source</li>
32582         * <li>rawEvent - Raw mouse event</li>
32583         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32584         * to be inserted by setting them on this object.</li>
32585         * <li>cancel - Set this to true to cancel the drop.</li>
32586         * </ul>
32587         * @param {Object} dropEvent
32588         */
32589        "beforenodedrop" : true,
32590        /**
32591         * @event nodedrop
32592         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32593         * passed to handlers has the following properties:<br />
32594         * <ul style="padding:5px;padding-left:16px;">
32595         * <li>tree - The TreePanel</li>
32596         * <li>target - The node being targeted for the drop</li>
32597         * <li>data - The drag data from the drag source</li>
32598         * <li>point - The point of the drop - append, above or below</li>
32599         * <li>source - The drag source</li>
32600         * <li>rawEvent - Raw mouse event</li>
32601         * <li>dropNode - Dropped node(s).</li>
32602         * </ul>
32603         * @param {Object} dropEvent
32604         */
32605        "nodedrop" : true,
32606         /**
32607         * @event nodedragover
32608         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32609         * passed to handlers has the following properties:<br />
32610         * <ul style="padding:5px;padding-left:16px;">
32611         * <li>tree - The TreePanel</li>
32612         * <li>target - The node being targeted for the drop</li>
32613         * <li>data - The drag data from the drag source</li>
32614         * <li>point - The point of the drop - append, above or below</li>
32615         * <li>source - The drag source</li>
32616         * <li>rawEvent - Raw mouse event</li>
32617         * <li>dropNode - Drop node(s) provided by the source.</li>
32618         * <li>cancel - Set this to true to signal drop not allowed.</li>
32619         * </ul>
32620         * @param {Object} dragOverEvent
32621         */
32622        "nodedragover" : true
32623         
32624     });
32625     if(this.singleExpand){
32626        this.on("beforeexpand", this.restrictExpand, this);
32627     }
32628     if (this.editor) {
32629         this.editor.tree = this;
32630         this.editor = Roo.factory(this.editor, Roo.tree);
32631     }
32632     
32633     if (this.selModel) {
32634         this.selModel = Roo.factory(this.selModel, Roo.tree);
32635     }
32636    
32637 };
32638 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32639     rootVisible : true,
32640     animate: Roo.enableFx,
32641     lines : true,
32642     enableDD : false,
32643     hlDrop : Roo.enableFx,
32644   
32645     renderer: false,
32646     
32647     rendererTip: false,
32648     // private
32649     restrictExpand : function(node){
32650         var p = node.parentNode;
32651         if(p){
32652             if(p.expandedChild && p.expandedChild.parentNode == p){
32653                 p.expandedChild.collapse();
32654             }
32655             p.expandedChild = node;
32656         }
32657     },
32658
32659     // private override
32660     setRootNode : function(node){
32661         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32662         if(!this.rootVisible){
32663             node.ui = new Roo.tree.RootTreeNodeUI(node);
32664         }
32665         return node;
32666     },
32667
32668     /**
32669      * Returns the container element for this TreePanel
32670      */
32671     getEl : function(){
32672         return this.el;
32673     },
32674
32675     /**
32676      * Returns the default TreeLoader for this TreePanel
32677      */
32678     getLoader : function(){
32679         return this.loader;
32680     },
32681
32682     /**
32683      * Expand all nodes
32684      */
32685     expandAll : function(){
32686         this.root.expand(true);
32687     },
32688
32689     /**
32690      * Collapse all nodes
32691      */
32692     collapseAll : function(){
32693         this.root.collapse(true);
32694     },
32695
32696     /**
32697      * Returns the selection model used by this TreePanel
32698      */
32699     getSelectionModel : function(){
32700         if(!this.selModel){
32701             this.selModel = new Roo.tree.DefaultSelectionModel();
32702         }
32703         return this.selModel;
32704     },
32705
32706     /**
32707      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32708      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32709      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32710      * @return {Array}
32711      */
32712     getChecked : function(a, startNode){
32713         startNode = startNode || this.root;
32714         var r = [];
32715         var f = function(){
32716             if(this.attributes.checked){
32717                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32718             }
32719         }
32720         startNode.cascade(f);
32721         return r;
32722     },
32723
32724     /**
32725      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32726      * @param {String} path
32727      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32728      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32729      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32730      */
32731     expandPath : function(path, attr, callback){
32732         attr = attr || "id";
32733         var keys = path.split(this.pathSeparator);
32734         var curNode = this.root;
32735         if(curNode.attributes[attr] != keys[1]){ // invalid root
32736             if(callback){
32737                 callback(false, null);
32738             }
32739             return;
32740         }
32741         var index = 1;
32742         var f = function(){
32743             if(++index == keys.length){
32744                 if(callback){
32745                     callback(true, curNode);
32746                 }
32747                 return;
32748             }
32749             var c = curNode.findChild(attr, keys[index]);
32750             if(!c){
32751                 if(callback){
32752                     callback(false, curNode);
32753                 }
32754                 return;
32755             }
32756             curNode = c;
32757             c.expand(false, false, f);
32758         };
32759         curNode.expand(false, false, f);
32760     },
32761
32762     /**
32763      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32764      * @param {String} path
32765      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32766      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32767      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32768      */
32769     selectPath : function(path, attr, callback){
32770         attr = attr || "id";
32771         var keys = path.split(this.pathSeparator);
32772         var v = keys.pop();
32773         if(keys.length > 0){
32774             var f = function(success, node){
32775                 if(success && node){
32776                     var n = node.findChild(attr, v);
32777                     if(n){
32778                         n.select();
32779                         if(callback){
32780                             callback(true, n);
32781                         }
32782                     }else if(callback){
32783                         callback(false, n);
32784                     }
32785                 }else{
32786                     if(callback){
32787                         callback(false, n);
32788                     }
32789                 }
32790             };
32791             this.expandPath(keys.join(this.pathSeparator), attr, f);
32792         }else{
32793             this.root.select();
32794             if(callback){
32795                 callback(true, this.root);
32796             }
32797         }
32798     },
32799
32800     getTreeEl : function(){
32801         return this.el;
32802     },
32803
32804     /**
32805      * Trigger rendering of this TreePanel
32806      */
32807     render : function(){
32808         if (this.innerCt) {
32809             return this; // stop it rendering more than once!!
32810         }
32811         
32812         this.innerCt = this.el.createChild({tag:"ul",
32813                cls:"x-tree-root-ct " +
32814                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32815
32816         if(this.containerScroll){
32817             Roo.dd.ScrollManager.register(this.el);
32818         }
32819         if((this.enableDD || this.enableDrop) && !this.dropZone){
32820            /**
32821             * The dropZone used by this tree if drop is enabled
32822             * @type Roo.tree.TreeDropZone
32823             */
32824              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32825                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32826            });
32827         }
32828         if((this.enableDD || this.enableDrag) && !this.dragZone){
32829            /**
32830             * The dragZone used by this tree if drag is enabled
32831             * @type Roo.tree.TreeDragZone
32832             */
32833             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32834                ddGroup: this.ddGroup || "TreeDD",
32835                scroll: this.ddScroll
32836            });
32837         }
32838         this.getSelectionModel().init(this);
32839         if (!this.root) {
32840             Roo.log("ROOT not set in tree");
32841             return this;
32842         }
32843         this.root.render();
32844         if(!this.rootVisible){
32845             this.root.renderChildren();
32846         }
32847         return this;
32848     }
32849 });/*
32850  * Based on:
32851  * Ext JS Library 1.1.1
32852  * Copyright(c) 2006-2007, Ext JS, LLC.
32853  *
32854  * Originally Released Under LGPL - original licence link has changed is not relivant.
32855  *
32856  * Fork - LGPL
32857  * <script type="text/javascript">
32858  */
32859  
32860
32861 /**
32862  * @class Roo.tree.DefaultSelectionModel
32863  * @extends Roo.util.Observable
32864  * The default single selection for a TreePanel.
32865  * @param {Object} cfg Configuration
32866  */
32867 Roo.tree.DefaultSelectionModel = function(cfg){
32868    this.selNode = null;
32869    
32870    
32871    
32872    this.addEvents({
32873        /**
32874         * @event selectionchange
32875         * Fires when the selected node changes
32876         * @param {DefaultSelectionModel} this
32877         * @param {TreeNode} node the new selection
32878         */
32879        "selectionchange" : true,
32880
32881        /**
32882         * @event beforeselect
32883         * Fires before the selected node changes, return false to cancel the change
32884         * @param {DefaultSelectionModel} this
32885         * @param {TreeNode} node the new selection
32886         * @param {TreeNode} node the old selection
32887         */
32888        "beforeselect" : true
32889    });
32890    
32891     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32892 };
32893
32894 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32895     init : function(tree){
32896         this.tree = tree;
32897         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32898         tree.on("click", this.onNodeClick, this);
32899     },
32900     
32901     onNodeClick : function(node, e){
32902         if (e.ctrlKey && this.selNode == node)  {
32903             this.unselect(node);
32904             return;
32905         }
32906         this.select(node);
32907     },
32908     
32909     /**
32910      * Select a node.
32911      * @param {TreeNode} node The node to select
32912      * @return {TreeNode} The selected node
32913      */
32914     select : function(node){
32915         var last = this.selNode;
32916         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32917             if(last){
32918                 last.ui.onSelectedChange(false);
32919             }
32920             this.selNode = node;
32921             node.ui.onSelectedChange(true);
32922             this.fireEvent("selectionchange", this, node, last);
32923         }
32924         return node;
32925     },
32926     
32927     /**
32928      * Deselect a node.
32929      * @param {TreeNode} node The node to unselect
32930      */
32931     unselect : function(node){
32932         if(this.selNode == node){
32933             this.clearSelections();
32934         }    
32935     },
32936     
32937     /**
32938      * Clear all selections
32939      */
32940     clearSelections : function(){
32941         var n = this.selNode;
32942         if(n){
32943             n.ui.onSelectedChange(false);
32944             this.selNode = null;
32945             this.fireEvent("selectionchange", this, null);
32946         }
32947         return n;
32948     },
32949     
32950     /**
32951      * Get the selected node
32952      * @return {TreeNode} The selected node
32953      */
32954     getSelectedNode : function(){
32955         return this.selNode;    
32956     },
32957     
32958     /**
32959      * Returns true if the node is selected
32960      * @param {TreeNode} node The node to check
32961      * @return {Boolean}
32962      */
32963     isSelected : function(node){
32964         return this.selNode == node;  
32965     },
32966
32967     /**
32968      * Selects the node above the selected node in the tree, intelligently walking the nodes
32969      * @return TreeNode The new selection
32970      */
32971     selectPrevious : function(){
32972         var s = this.selNode || this.lastSelNode;
32973         if(!s){
32974             return null;
32975         }
32976         var ps = s.previousSibling;
32977         if(ps){
32978             if(!ps.isExpanded() || ps.childNodes.length < 1){
32979                 return this.select(ps);
32980             } else{
32981                 var lc = ps.lastChild;
32982                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32983                     lc = lc.lastChild;
32984                 }
32985                 return this.select(lc);
32986             }
32987         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32988             return this.select(s.parentNode);
32989         }
32990         return null;
32991     },
32992
32993     /**
32994      * Selects the node above the selected node in the tree, intelligently walking the nodes
32995      * @return TreeNode The new selection
32996      */
32997     selectNext : function(){
32998         var s = this.selNode || this.lastSelNode;
32999         if(!s){
33000             return null;
33001         }
33002         if(s.firstChild && s.isExpanded()){
33003              return this.select(s.firstChild);
33004          }else if(s.nextSibling){
33005              return this.select(s.nextSibling);
33006          }else if(s.parentNode){
33007             var newS = null;
33008             s.parentNode.bubble(function(){
33009                 if(this.nextSibling){
33010                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
33011                     return false;
33012                 }
33013             });
33014             return newS;
33015          }
33016         return null;
33017     },
33018
33019     onKeyDown : function(e){
33020         var s = this.selNode || this.lastSelNode;
33021         // undesirable, but required
33022         var sm = this;
33023         if(!s){
33024             return;
33025         }
33026         var k = e.getKey();
33027         switch(k){
33028              case e.DOWN:
33029                  e.stopEvent();
33030                  this.selectNext();
33031              break;
33032              case e.UP:
33033                  e.stopEvent();
33034                  this.selectPrevious();
33035              break;
33036              case e.RIGHT:
33037                  e.preventDefault();
33038                  if(s.hasChildNodes()){
33039                      if(!s.isExpanded()){
33040                          s.expand();
33041                      }else if(s.firstChild){
33042                          this.select(s.firstChild, e);
33043                      }
33044                  }
33045              break;
33046              case e.LEFT:
33047                  e.preventDefault();
33048                  if(s.hasChildNodes() && s.isExpanded()){
33049                      s.collapse();
33050                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33051                      this.select(s.parentNode, e);
33052                  }
33053              break;
33054         };
33055     }
33056 });
33057
33058 /**
33059  * @class Roo.tree.MultiSelectionModel
33060  * @extends Roo.util.Observable
33061  * Multi selection for a TreePanel.
33062  * @param {Object} cfg Configuration
33063  */
33064 Roo.tree.MultiSelectionModel = function(){
33065    this.selNodes = [];
33066    this.selMap = {};
33067    this.addEvents({
33068        /**
33069         * @event selectionchange
33070         * Fires when the selected nodes change
33071         * @param {MultiSelectionModel} this
33072         * @param {Array} nodes Array of the selected nodes
33073         */
33074        "selectionchange" : true
33075    });
33076    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33077    
33078 };
33079
33080 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33081     init : function(tree){
33082         this.tree = tree;
33083         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33084         tree.on("click", this.onNodeClick, this);
33085     },
33086     
33087     onNodeClick : function(node, e){
33088         this.select(node, e, e.ctrlKey);
33089     },
33090     
33091     /**
33092      * Select a node.
33093      * @param {TreeNode} node The node to select
33094      * @param {EventObject} e (optional) An event associated with the selection
33095      * @param {Boolean} keepExisting True to retain existing selections
33096      * @return {TreeNode} The selected node
33097      */
33098     select : function(node, e, keepExisting){
33099         if(keepExisting !== true){
33100             this.clearSelections(true);
33101         }
33102         if(this.isSelected(node)){
33103             this.lastSelNode = node;
33104             return node;
33105         }
33106         this.selNodes.push(node);
33107         this.selMap[node.id] = node;
33108         this.lastSelNode = node;
33109         node.ui.onSelectedChange(true);
33110         this.fireEvent("selectionchange", this, this.selNodes);
33111         return node;
33112     },
33113     
33114     /**
33115      * Deselect a node.
33116      * @param {TreeNode} node The node to unselect
33117      */
33118     unselect : function(node){
33119         if(this.selMap[node.id]){
33120             node.ui.onSelectedChange(false);
33121             var sn = this.selNodes;
33122             var index = -1;
33123             if(sn.indexOf){
33124                 index = sn.indexOf(node);
33125             }else{
33126                 for(var i = 0, len = sn.length; i < len; i++){
33127                     if(sn[i] == node){
33128                         index = i;
33129                         break;
33130                     }
33131                 }
33132             }
33133             if(index != -1){
33134                 this.selNodes.splice(index, 1);
33135             }
33136             delete this.selMap[node.id];
33137             this.fireEvent("selectionchange", this, this.selNodes);
33138         }
33139     },
33140     
33141     /**
33142      * Clear all selections
33143      */
33144     clearSelections : function(suppressEvent){
33145         var sn = this.selNodes;
33146         if(sn.length > 0){
33147             for(var i = 0, len = sn.length; i < len; i++){
33148                 sn[i].ui.onSelectedChange(false);
33149             }
33150             this.selNodes = [];
33151             this.selMap = {};
33152             if(suppressEvent !== true){
33153                 this.fireEvent("selectionchange", this, this.selNodes);
33154             }
33155         }
33156     },
33157     
33158     /**
33159      * Returns true if the node is selected
33160      * @param {TreeNode} node The node to check
33161      * @return {Boolean}
33162      */
33163     isSelected : function(node){
33164         return this.selMap[node.id] ? true : false;  
33165     },
33166     
33167     /**
33168      * Returns an array of the selected nodes
33169      * @return {Array}
33170      */
33171     getSelectedNodes : function(){
33172         return this.selNodes;    
33173     },
33174
33175     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33176
33177     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33178
33179     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33180 });/*
33181  * Based on:
33182  * Ext JS Library 1.1.1
33183  * Copyright(c) 2006-2007, Ext JS, LLC.
33184  *
33185  * Originally Released Under LGPL - original licence link has changed is not relivant.
33186  *
33187  * Fork - LGPL
33188  * <script type="text/javascript">
33189  */
33190  
33191 /**
33192  * @class Roo.tree.TreeNode
33193  * @extends Roo.data.Node
33194  * @cfg {String} text The text for this node
33195  * @cfg {Boolean} expanded true to start the node expanded
33196  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33197  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33198  * @cfg {Boolean} disabled true to start the node disabled
33199  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33200  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33201  * @cfg {String} cls A css class to be added to the node
33202  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33203  * @cfg {String} href URL of the link used for the node (defaults to #)
33204  * @cfg {String} hrefTarget target frame for the link
33205  * @cfg {String} qtip An Ext QuickTip for the node
33206  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33207  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33208  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33209  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33210  * (defaults to undefined with no checkbox rendered)
33211  * @constructor
33212  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33213  */
33214 Roo.tree.TreeNode = function(attributes){
33215     attributes = attributes || {};
33216     if(typeof attributes == "string"){
33217         attributes = {text: attributes};
33218     }
33219     this.childrenRendered = false;
33220     this.rendered = false;
33221     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33222     this.expanded = attributes.expanded === true;
33223     this.isTarget = attributes.isTarget !== false;
33224     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33225     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33226
33227     /**
33228      * Read-only. The text for this node. To change it use setText().
33229      * @type String
33230      */
33231     this.text = attributes.text;
33232     /**
33233      * True if this node is disabled.
33234      * @type Boolean
33235      */
33236     this.disabled = attributes.disabled === true;
33237
33238     this.addEvents({
33239         /**
33240         * @event textchange
33241         * Fires when the text for this node is changed
33242         * @param {Node} this This node
33243         * @param {String} text The new text
33244         * @param {String} oldText The old text
33245         */
33246         "textchange" : true,
33247         /**
33248         * @event beforeexpand
33249         * Fires before this node is expanded, return false to cancel.
33250         * @param {Node} this This node
33251         * @param {Boolean} deep
33252         * @param {Boolean} anim
33253         */
33254         "beforeexpand" : true,
33255         /**
33256         * @event beforecollapse
33257         * Fires before this node is collapsed, return false to cancel.
33258         * @param {Node} this This node
33259         * @param {Boolean} deep
33260         * @param {Boolean} anim
33261         */
33262         "beforecollapse" : true,
33263         /**
33264         * @event expand
33265         * Fires when this node is expanded
33266         * @param {Node} this This node
33267         */
33268         "expand" : true,
33269         /**
33270         * @event disabledchange
33271         * Fires when the disabled status of this node changes
33272         * @param {Node} this This node
33273         * @param {Boolean} disabled
33274         */
33275         "disabledchange" : true,
33276         /**
33277         * @event collapse
33278         * Fires when this node is collapsed
33279         * @param {Node} this This node
33280         */
33281         "collapse" : true,
33282         /**
33283         * @event beforeclick
33284         * Fires before click processing. Return false to cancel the default action.
33285         * @param {Node} this This node
33286         * @param {Roo.EventObject} e The event object
33287         */
33288         "beforeclick":true,
33289         /**
33290         * @event checkchange
33291         * Fires when a node with a checkbox's checked property changes
33292         * @param {Node} this This node
33293         * @param {Boolean} checked
33294         */
33295         "checkchange":true,
33296         /**
33297         * @event click
33298         * Fires when this node is clicked
33299         * @param {Node} this This node
33300         * @param {Roo.EventObject} e The event object
33301         */
33302         "click":true,
33303         /**
33304         * @event dblclick
33305         * Fires when this node is double clicked
33306         * @param {Node} this This node
33307         * @param {Roo.EventObject} e The event object
33308         */
33309         "dblclick":true,
33310         /**
33311         * @event contextmenu
33312         * Fires when this node is right clicked
33313         * @param {Node} this This node
33314         * @param {Roo.EventObject} e The event object
33315         */
33316         "contextmenu":true,
33317         /**
33318         * @event beforechildrenrendered
33319         * Fires right before the child nodes for this node are rendered
33320         * @param {Node} this This node
33321         */
33322         "beforechildrenrendered":true
33323     });
33324
33325     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33326
33327     /**
33328      * Read-only. The UI for this node
33329      * @type TreeNodeUI
33330      */
33331     this.ui = new uiClass(this);
33332     
33333     // finally support items[]
33334     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33335         return;
33336     }
33337     
33338     
33339     Roo.each(this.attributes.items, function(c) {
33340         this.appendChild(Roo.factory(c,Roo.Tree));
33341     }, this);
33342     delete this.attributes.items;
33343     
33344     
33345     
33346 };
33347 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33348     preventHScroll: true,
33349     /**
33350      * Returns true if this node is expanded
33351      * @return {Boolean}
33352      */
33353     isExpanded : function(){
33354         return this.expanded;
33355     },
33356
33357     /**
33358      * Returns the UI object for this node
33359      * @return {TreeNodeUI}
33360      */
33361     getUI : function(){
33362         return this.ui;
33363     },
33364
33365     // private override
33366     setFirstChild : function(node){
33367         var of = this.firstChild;
33368         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33369         if(this.childrenRendered && of && node != of){
33370             of.renderIndent(true, true);
33371         }
33372         if(this.rendered){
33373             this.renderIndent(true, true);
33374         }
33375     },
33376
33377     // private override
33378     setLastChild : function(node){
33379         var ol = this.lastChild;
33380         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33381         if(this.childrenRendered && ol && node != ol){
33382             ol.renderIndent(true, true);
33383         }
33384         if(this.rendered){
33385             this.renderIndent(true, true);
33386         }
33387     },
33388
33389     // these methods are overridden to provide lazy rendering support
33390     // private override
33391     appendChild : function()
33392     {
33393         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33394         if(node && this.childrenRendered){
33395             node.render();
33396         }
33397         this.ui.updateExpandIcon();
33398         return node;
33399     },
33400
33401     // private override
33402     removeChild : function(node){
33403         this.ownerTree.getSelectionModel().unselect(node);
33404         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33405         // if it's been rendered remove dom node
33406         if(this.childrenRendered){
33407             node.ui.remove();
33408         }
33409         if(this.childNodes.length < 1){
33410             this.collapse(false, false);
33411         }else{
33412             this.ui.updateExpandIcon();
33413         }
33414         if(!this.firstChild) {
33415             this.childrenRendered = false;
33416         }
33417         return node;
33418     },
33419
33420     // private override
33421     insertBefore : function(node, refNode){
33422         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33423         if(newNode && refNode && this.childrenRendered){
33424             node.render();
33425         }
33426         this.ui.updateExpandIcon();
33427         return newNode;
33428     },
33429
33430     /**
33431      * Sets the text for this node
33432      * @param {String} text
33433      */
33434     setText : function(text){
33435         var oldText = this.text;
33436         this.text = text;
33437         this.attributes.text = text;
33438         if(this.rendered){ // event without subscribing
33439             this.ui.onTextChange(this, text, oldText);
33440         }
33441         this.fireEvent("textchange", this, text, oldText);
33442     },
33443
33444     /**
33445      * Triggers selection of this node
33446      */
33447     select : function(){
33448         this.getOwnerTree().getSelectionModel().select(this);
33449     },
33450
33451     /**
33452      * Triggers deselection of this node
33453      */
33454     unselect : function(){
33455         this.getOwnerTree().getSelectionModel().unselect(this);
33456     },
33457
33458     /**
33459      * Returns true if this node is selected
33460      * @return {Boolean}
33461      */
33462     isSelected : function(){
33463         return this.getOwnerTree().getSelectionModel().isSelected(this);
33464     },
33465
33466     /**
33467      * Expand this node.
33468      * @param {Boolean} deep (optional) True to expand all children as well
33469      * @param {Boolean} anim (optional) false to cancel the default animation
33470      * @param {Function} callback (optional) A callback to be called when
33471      * expanding this node completes (does not wait for deep expand to complete).
33472      * Called with 1 parameter, this node.
33473      */
33474     expand : function(deep, anim, callback){
33475         if(!this.expanded){
33476             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33477                 return;
33478             }
33479             if(!this.childrenRendered){
33480                 this.renderChildren();
33481             }
33482             this.expanded = true;
33483             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33484                 this.ui.animExpand(function(){
33485                     this.fireEvent("expand", this);
33486                     if(typeof callback == "function"){
33487                         callback(this);
33488                     }
33489                     if(deep === true){
33490                         this.expandChildNodes(true);
33491                     }
33492                 }.createDelegate(this));
33493                 return;
33494             }else{
33495                 this.ui.expand();
33496                 this.fireEvent("expand", this);
33497                 if(typeof callback == "function"){
33498                     callback(this);
33499                 }
33500             }
33501         }else{
33502            if(typeof callback == "function"){
33503                callback(this);
33504            }
33505         }
33506         if(deep === true){
33507             this.expandChildNodes(true);
33508         }
33509     },
33510
33511     isHiddenRoot : function(){
33512         return this.isRoot && !this.getOwnerTree().rootVisible;
33513     },
33514
33515     /**
33516      * Collapse this node.
33517      * @param {Boolean} deep (optional) True to collapse all children as well
33518      * @param {Boolean} anim (optional) false to cancel the default animation
33519      */
33520     collapse : function(deep, anim){
33521         if(this.expanded && !this.isHiddenRoot()){
33522             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33523                 return;
33524             }
33525             this.expanded = false;
33526             if((this.getOwnerTree().animate && anim !== false) || anim){
33527                 this.ui.animCollapse(function(){
33528                     this.fireEvent("collapse", this);
33529                     if(deep === true){
33530                         this.collapseChildNodes(true);
33531                     }
33532                 }.createDelegate(this));
33533                 return;
33534             }else{
33535                 this.ui.collapse();
33536                 this.fireEvent("collapse", this);
33537             }
33538         }
33539         if(deep === true){
33540             var cs = this.childNodes;
33541             for(var i = 0, len = cs.length; i < len; i++) {
33542                 cs[i].collapse(true, false);
33543             }
33544         }
33545     },
33546
33547     // private
33548     delayedExpand : function(delay){
33549         if(!this.expandProcId){
33550             this.expandProcId = this.expand.defer(delay, this);
33551         }
33552     },
33553
33554     // private
33555     cancelExpand : function(){
33556         if(this.expandProcId){
33557             clearTimeout(this.expandProcId);
33558         }
33559         this.expandProcId = false;
33560     },
33561
33562     /**
33563      * Toggles expanded/collapsed state of the node
33564      */
33565     toggle : function(){
33566         if(this.expanded){
33567             this.collapse();
33568         }else{
33569             this.expand();
33570         }
33571     },
33572
33573     /**
33574      * Ensures all parent nodes are expanded
33575      */
33576     ensureVisible : function(callback){
33577         var tree = this.getOwnerTree();
33578         tree.expandPath(this.parentNode.getPath(), false, function(){
33579             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33580             Roo.callback(callback);
33581         }.createDelegate(this));
33582     },
33583
33584     /**
33585      * Expand all child nodes
33586      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33587      */
33588     expandChildNodes : function(deep){
33589         var cs = this.childNodes;
33590         for(var i = 0, len = cs.length; i < len; i++) {
33591                 cs[i].expand(deep);
33592         }
33593     },
33594
33595     /**
33596      * Collapse all child nodes
33597      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33598      */
33599     collapseChildNodes : function(deep){
33600         var cs = this.childNodes;
33601         for(var i = 0, len = cs.length; i < len; i++) {
33602                 cs[i].collapse(deep);
33603         }
33604     },
33605
33606     /**
33607      * Disables this node
33608      */
33609     disable : function(){
33610         this.disabled = true;
33611         this.unselect();
33612         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33613             this.ui.onDisableChange(this, true);
33614         }
33615         this.fireEvent("disabledchange", this, true);
33616     },
33617
33618     /**
33619      * Enables this node
33620      */
33621     enable : function(){
33622         this.disabled = false;
33623         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33624             this.ui.onDisableChange(this, false);
33625         }
33626         this.fireEvent("disabledchange", this, false);
33627     },
33628
33629     // private
33630     renderChildren : function(suppressEvent){
33631         if(suppressEvent !== false){
33632             this.fireEvent("beforechildrenrendered", this);
33633         }
33634         var cs = this.childNodes;
33635         for(var i = 0, len = cs.length; i < len; i++){
33636             cs[i].render(true);
33637         }
33638         this.childrenRendered = true;
33639     },
33640
33641     // private
33642     sort : function(fn, scope){
33643         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33644         if(this.childrenRendered){
33645             var cs = this.childNodes;
33646             for(var i = 0, len = cs.length; i < len; i++){
33647                 cs[i].render(true);
33648             }
33649         }
33650     },
33651
33652     // private
33653     render : function(bulkRender){
33654         this.ui.render(bulkRender);
33655         if(!this.rendered){
33656             this.rendered = true;
33657             if(this.expanded){
33658                 this.expanded = false;
33659                 this.expand(false, false);
33660             }
33661         }
33662     },
33663
33664     // private
33665     renderIndent : function(deep, refresh){
33666         if(refresh){
33667             this.ui.childIndent = null;
33668         }
33669         this.ui.renderIndent();
33670         if(deep === true && this.childrenRendered){
33671             var cs = this.childNodes;
33672             for(var i = 0, len = cs.length; i < len; i++){
33673                 cs[i].renderIndent(true, refresh);
33674             }
33675         }
33676     }
33677 });/*
33678  * Based on:
33679  * Ext JS Library 1.1.1
33680  * Copyright(c) 2006-2007, Ext JS, LLC.
33681  *
33682  * Originally Released Under LGPL - original licence link has changed is not relivant.
33683  *
33684  * Fork - LGPL
33685  * <script type="text/javascript">
33686  */
33687  
33688 /**
33689  * @class Roo.tree.AsyncTreeNode
33690  * @extends Roo.tree.TreeNode
33691  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33692  * @constructor
33693  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33694  */
33695  Roo.tree.AsyncTreeNode = function(config){
33696     this.loaded = false;
33697     this.loading = false;
33698     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33699     /**
33700     * @event beforeload
33701     * Fires before this node is loaded, return false to cancel
33702     * @param {Node} this This node
33703     */
33704     this.addEvents({'beforeload':true, 'load': true});
33705     /**
33706     * @event load
33707     * Fires when this node is loaded
33708     * @param {Node} this This node
33709     */
33710     /**
33711      * The loader used by this node (defaults to using the tree's defined loader)
33712      * @type TreeLoader
33713      * @property loader
33714      */
33715 };
33716 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33717     expand : function(deep, anim, callback){
33718         if(this.loading){ // if an async load is already running, waiting til it's done
33719             var timer;
33720             var f = function(){
33721                 if(!this.loading){ // done loading
33722                     clearInterval(timer);
33723                     this.expand(deep, anim, callback);
33724                 }
33725             }.createDelegate(this);
33726             timer = setInterval(f, 200);
33727             return;
33728         }
33729         if(!this.loaded){
33730             if(this.fireEvent("beforeload", this) === false){
33731                 return;
33732             }
33733             this.loading = true;
33734             this.ui.beforeLoad(this);
33735             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33736             if(loader){
33737                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33738                 return;
33739             }
33740         }
33741         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33742     },
33743     
33744     /**
33745      * Returns true if this node is currently loading
33746      * @return {Boolean}
33747      */
33748     isLoading : function(){
33749         return this.loading;  
33750     },
33751     
33752     loadComplete : function(deep, anim, callback){
33753         this.loading = false;
33754         this.loaded = true;
33755         this.ui.afterLoad(this);
33756         this.fireEvent("load", this);
33757         this.expand(deep, anim, callback);
33758     },
33759     
33760     /**
33761      * Returns true if this node has been loaded
33762      * @return {Boolean}
33763      */
33764     isLoaded : function(){
33765         return this.loaded;
33766     },
33767     
33768     hasChildNodes : function(){
33769         if(!this.isLeaf() && !this.loaded){
33770             return true;
33771         }else{
33772             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33773         }
33774     },
33775
33776     /**
33777      * Trigger a reload for this node
33778      * @param {Function} callback
33779      */
33780     reload : function(callback){
33781         this.collapse(false, false);
33782         while(this.firstChild){
33783             this.removeChild(this.firstChild);
33784         }
33785         this.childrenRendered = false;
33786         this.loaded = false;
33787         if(this.isHiddenRoot()){
33788             this.expanded = false;
33789         }
33790         this.expand(false, false, callback);
33791     }
33792 });/*
33793  * Based on:
33794  * Ext JS Library 1.1.1
33795  * Copyright(c) 2006-2007, Ext JS, LLC.
33796  *
33797  * Originally Released Under LGPL - original licence link has changed is not relivant.
33798  *
33799  * Fork - LGPL
33800  * <script type="text/javascript">
33801  */
33802  
33803 /**
33804  * @class Roo.tree.TreeNodeUI
33805  * @constructor
33806  * @param {Object} node The node to render
33807  * The TreeNode UI implementation is separate from the
33808  * tree implementation. Unless you are customizing the tree UI,
33809  * you should never have to use this directly.
33810  */
33811 Roo.tree.TreeNodeUI = function(node){
33812     this.node = node;
33813     this.rendered = false;
33814     this.animating = false;
33815     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33816 };
33817
33818 Roo.tree.TreeNodeUI.prototype = {
33819     removeChild : function(node){
33820         if(this.rendered){
33821             this.ctNode.removeChild(node.ui.getEl());
33822         }
33823     },
33824
33825     beforeLoad : function(){
33826          this.addClass("x-tree-node-loading");
33827     },
33828
33829     afterLoad : function(){
33830          this.removeClass("x-tree-node-loading");
33831     },
33832
33833     onTextChange : function(node, text, oldText){
33834         if(this.rendered){
33835             this.textNode.innerHTML = text;
33836         }
33837     },
33838
33839     onDisableChange : function(node, state){
33840         this.disabled = state;
33841         if(state){
33842             this.addClass("x-tree-node-disabled");
33843         }else{
33844             this.removeClass("x-tree-node-disabled");
33845         }
33846     },
33847
33848     onSelectedChange : function(state){
33849         if(state){
33850             this.focus();
33851             this.addClass("x-tree-selected");
33852         }else{
33853             //this.blur();
33854             this.removeClass("x-tree-selected");
33855         }
33856     },
33857
33858     onMove : function(tree, node, oldParent, newParent, index, refNode){
33859         this.childIndent = null;
33860         if(this.rendered){
33861             var targetNode = newParent.ui.getContainer();
33862             if(!targetNode){//target not rendered
33863                 this.holder = document.createElement("div");
33864                 this.holder.appendChild(this.wrap);
33865                 return;
33866             }
33867             var insertBefore = refNode ? refNode.ui.getEl() : null;
33868             if(insertBefore){
33869                 targetNode.insertBefore(this.wrap, insertBefore);
33870             }else{
33871                 targetNode.appendChild(this.wrap);
33872             }
33873             this.node.renderIndent(true);
33874         }
33875     },
33876
33877     addClass : function(cls){
33878         if(this.elNode){
33879             Roo.fly(this.elNode).addClass(cls);
33880         }
33881     },
33882
33883     removeClass : function(cls){
33884         if(this.elNode){
33885             Roo.fly(this.elNode).removeClass(cls);
33886         }
33887     },
33888
33889     remove : function(){
33890         if(this.rendered){
33891             this.holder = document.createElement("div");
33892             this.holder.appendChild(this.wrap);
33893         }
33894     },
33895
33896     fireEvent : function(){
33897         return this.node.fireEvent.apply(this.node, arguments);
33898     },
33899
33900     initEvents : function(){
33901         this.node.on("move", this.onMove, this);
33902         var E = Roo.EventManager;
33903         var a = this.anchor;
33904
33905         var el = Roo.fly(a, '_treeui');
33906
33907         if(Roo.isOpera){ // opera render bug ignores the CSS
33908             el.setStyle("text-decoration", "none");
33909         }
33910
33911         el.on("click", this.onClick, this);
33912         el.on("dblclick", this.onDblClick, this);
33913
33914         if(this.checkbox){
33915             Roo.EventManager.on(this.checkbox,
33916                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33917         }
33918
33919         el.on("contextmenu", this.onContextMenu, this);
33920
33921         var icon = Roo.fly(this.iconNode);
33922         icon.on("click", this.onClick, this);
33923         icon.on("dblclick", this.onDblClick, this);
33924         icon.on("contextmenu", this.onContextMenu, this);
33925         E.on(this.ecNode, "click", this.ecClick, this, true);
33926
33927         if(this.node.disabled){
33928             this.addClass("x-tree-node-disabled");
33929         }
33930         if(this.node.hidden){
33931             this.addClass("x-tree-node-disabled");
33932         }
33933         var ot = this.node.getOwnerTree();
33934         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33935         if(dd && (!this.node.isRoot || ot.rootVisible)){
33936             Roo.dd.Registry.register(this.elNode, {
33937                 node: this.node,
33938                 handles: this.getDDHandles(),
33939                 isHandle: false
33940             });
33941         }
33942     },
33943
33944     getDDHandles : function(){
33945         return [this.iconNode, this.textNode];
33946     },
33947
33948     hide : function(){
33949         if(this.rendered){
33950             this.wrap.style.display = "none";
33951         }
33952     },
33953
33954     show : function(){
33955         if(this.rendered){
33956             this.wrap.style.display = "";
33957         }
33958     },
33959
33960     onContextMenu : function(e){
33961         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33962             e.preventDefault();
33963             this.focus();
33964             this.fireEvent("contextmenu", this.node, e);
33965         }
33966     },
33967
33968     onClick : function(e){
33969         if(this.dropping){
33970             e.stopEvent();
33971             return;
33972         }
33973         if(this.fireEvent("beforeclick", this.node, e) !== false){
33974             if(!this.disabled && this.node.attributes.href){
33975                 this.fireEvent("click", this.node, e);
33976                 return;
33977             }
33978             e.preventDefault();
33979             if(this.disabled){
33980                 return;
33981             }
33982
33983             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33984                 this.node.toggle();
33985             }
33986
33987             this.fireEvent("click", this.node, e);
33988         }else{
33989             e.stopEvent();
33990         }
33991     },
33992
33993     onDblClick : function(e){
33994         e.preventDefault();
33995         if(this.disabled){
33996             return;
33997         }
33998         if(this.checkbox){
33999             this.toggleCheck();
34000         }
34001         if(!this.animating && this.node.hasChildNodes()){
34002             this.node.toggle();
34003         }
34004         this.fireEvent("dblclick", this.node, e);
34005     },
34006
34007     onCheckChange : function(){
34008         var checked = this.checkbox.checked;
34009         this.node.attributes.checked = checked;
34010         this.fireEvent('checkchange', this.node, checked);
34011     },
34012
34013     ecClick : function(e){
34014         if(!this.animating && this.node.hasChildNodes()){
34015             this.node.toggle();
34016         }
34017     },
34018
34019     startDrop : function(){
34020         this.dropping = true;
34021     },
34022
34023     // delayed drop so the click event doesn't get fired on a drop
34024     endDrop : function(){
34025        setTimeout(function(){
34026            this.dropping = false;
34027        }.createDelegate(this), 50);
34028     },
34029
34030     expand : function(){
34031         this.updateExpandIcon();
34032         this.ctNode.style.display = "";
34033     },
34034
34035     focus : function(){
34036         if(!this.node.preventHScroll){
34037             try{this.anchor.focus();
34038             }catch(e){}
34039         }else if(!Roo.isIE){
34040             try{
34041                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34042                 var l = noscroll.scrollLeft;
34043                 this.anchor.focus();
34044                 noscroll.scrollLeft = l;
34045             }catch(e){}
34046         }
34047     },
34048
34049     toggleCheck : function(value){
34050         var cb = this.checkbox;
34051         if(cb){
34052             cb.checked = (value === undefined ? !cb.checked : value);
34053         }
34054     },
34055
34056     blur : function(){
34057         try{
34058             this.anchor.blur();
34059         }catch(e){}
34060     },
34061
34062     animExpand : function(callback){
34063         var ct = Roo.get(this.ctNode);
34064         ct.stopFx();
34065         if(!this.node.hasChildNodes()){
34066             this.updateExpandIcon();
34067             this.ctNode.style.display = "";
34068             Roo.callback(callback);
34069             return;
34070         }
34071         this.animating = true;
34072         this.updateExpandIcon();
34073
34074         ct.slideIn('t', {
34075            callback : function(){
34076                this.animating = false;
34077                Roo.callback(callback);
34078             },
34079             scope: this,
34080             duration: this.node.ownerTree.duration || .25
34081         });
34082     },
34083
34084     highlight : function(){
34085         var tree = this.node.getOwnerTree();
34086         Roo.fly(this.wrap).highlight(
34087             tree.hlColor || "C3DAF9",
34088             {endColor: tree.hlBaseColor}
34089         );
34090     },
34091
34092     collapse : function(){
34093         this.updateExpandIcon();
34094         this.ctNode.style.display = "none";
34095     },
34096
34097     animCollapse : function(callback){
34098         var ct = Roo.get(this.ctNode);
34099         ct.enableDisplayMode('block');
34100         ct.stopFx();
34101
34102         this.animating = true;
34103         this.updateExpandIcon();
34104
34105         ct.slideOut('t', {
34106             callback : function(){
34107                this.animating = false;
34108                Roo.callback(callback);
34109             },
34110             scope: this,
34111             duration: this.node.ownerTree.duration || .25
34112         });
34113     },
34114
34115     getContainer : function(){
34116         return this.ctNode;
34117     },
34118
34119     getEl : function(){
34120         return this.wrap;
34121     },
34122
34123     appendDDGhost : function(ghostNode){
34124         ghostNode.appendChild(this.elNode.cloneNode(true));
34125     },
34126
34127     getDDRepairXY : function(){
34128         return Roo.lib.Dom.getXY(this.iconNode);
34129     },
34130
34131     onRender : function(){
34132         this.render();
34133     },
34134
34135     render : function(bulkRender){
34136         var n = this.node, a = n.attributes;
34137         var targetNode = n.parentNode ?
34138               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34139
34140         if(!this.rendered){
34141             this.rendered = true;
34142
34143             this.renderElements(n, a, targetNode, bulkRender);
34144
34145             if(a.qtip){
34146                if(this.textNode.setAttributeNS){
34147                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34148                    if(a.qtipTitle){
34149                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34150                    }
34151                }else{
34152                    this.textNode.setAttribute("ext:qtip", a.qtip);
34153                    if(a.qtipTitle){
34154                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34155                    }
34156                }
34157             }else if(a.qtipCfg){
34158                 a.qtipCfg.target = Roo.id(this.textNode);
34159                 Roo.QuickTips.register(a.qtipCfg);
34160             }
34161             this.initEvents();
34162             if(!this.node.expanded){
34163                 this.updateExpandIcon();
34164             }
34165         }else{
34166             if(bulkRender === true) {
34167                 targetNode.appendChild(this.wrap);
34168             }
34169         }
34170     },
34171
34172     renderElements : function(n, a, targetNode, bulkRender)
34173     {
34174         // add some indent caching, this helps performance when rendering a large tree
34175         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34176         var t = n.getOwnerTree();
34177         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34178         if (typeof(n.attributes.html) != 'undefined') {
34179             txt = n.attributes.html;
34180         }
34181         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34182         var cb = typeof a.checked == 'boolean';
34183         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34184         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34185             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34186             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34187             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34188             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34189             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34190              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34191                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34192             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34193             "</li>"];
34194
34195         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34196             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34197                                 n.nextSibling.ui.getEl(), buf.join(""));
34198         }else{
34199             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34200         }
34201
34202         this.elNode = this.wrap.childNodes[0];
34203         this.ctNode = this.wrap.childNodes[1];
34204         var cs = this.elNode.childNodes;
34205         this.indentNode = cs[0];
34206         this.ecNode = cs[1];
34207         this.iconNode = cs[2];
34208         var index = 3;
34209         if(cb){
34210             this.checkbox = cs[3];
34211             index++;
34212         }
34213         this.anchor = cs[index];
34214         this.textNode = cs[index].firstChild;
34215     },
34216
34217     getAnchor : function(){
34218         return this.anchor;
34219     },
34220
34221     getTextEl : function(){
34222         return this.textNode;
34223     },
34224
34225     getIconEl : function(){
34226         return this.iconNode;
34227     },
34228
34229     isChecked : function(){
34230         return this.checkbox ? this.checkbox.checked : false;
34231     },
34232
34233     updateExpandIcon : function(){
34234         if(this.rendered){
34235             var n = this.node, c1, c2;
34236             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34237             var hasChild = n.hasChildNodes();
34238             if(hasChild){
34239                 if(n.expanded){
34240                     cls += "-minus";
34241                     c1 = "x-tree-node-collapsed";
34242                     c2 = "x-tree-node-expanded";
34243                 }else{
34244                     cls += "-plus";
34245                     c1 = "x-tree-node-expanded";
34246                     c2 = "x-tree-node-collapsed";
34247                 }
34248                 if(this.wasLeaf){
34249                     this.removeClass("x-tree-node-leaf");
34250                     this.wasLeaf = false;
34251                 }
34252                 if(this.c1 != c1 || this.c2 != c2){
34253                     Roo.fly(this.elNode).replaceClass(c1, c2);
34254                     this.c1 = c1; this.c2 = c2;
34255                 }
34256             }else{
34257                 // this changes non-leafs into leafs if they have no children.
34258                 // it's not very rational behaviour..
34259                 
34260                 if(!this.wasLeaf && this.node.leaf){
34261                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34262                     delete this.c1;
34263                     delete this.c2;
34264                     this.wasLeaf = true;
34265                 }
34266             }
34267             var ecc = "x-tree-ec-icon "+cls;
34268             if(this.ecc != ecc){
34269                 this.ecNode.className = ecc;
34270                 this.ecc = ecc;
34271             }
34272         }
34273     },
34274
34275     getChildIndent : function(){
34276         if(!this.childIndent){
34277             var buf = [];
34278             var p = this.node;
34279             while(p){
34280                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34281                     if(!p.isLast()) {
34282                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34283                     } else {
34284                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34285                     }
34286                 }
34287                 p = p.parentNode;
34288             }
34289             this.childIndent = buf.join("");
34290         }
34291         return this.childIndent;
34292     },
34293
34294     renderIndent : function(){
34295         if(this.rendered){
34296             var indent = "";
34297             var p = this.node.parentNode;
34298             if(p){
34299                 indent = p.ui.getChildIndent();
34300             }
34301             if(this.indentMarkup != indent){ // don't rerender if not required
34302                 this.indentNode.innerHTML = indent;
34303                 this.indentMarkup = indent;
34304             }
34305             this.updateExpandIcon();
34306         }
34307     }
34308 };
34309
34310 Roo.tree.RootTreeNodeUI = function(){
34311     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34312 };
34313 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34314     render : function(){
34315         if(!this.rendered){
34316             var targetNode = this.node.ownerTree.innerCt.dom;
34317             this.node.expanded = true;
34318             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34319             this.wrap = this.ctNode = targetNode.firstChild;
34320         }
34321     },
34322     collapse : function(){
34323     },
34324     expand : function(){
34325     }
34326 });/*
34327  * Based on:
34328  * Ext JS Library 1.1.1
34329  * Copyright(c) 2006-2007, Ext JS, LLC.
34330  *
34331  * Originally Released Under LGPL - original licence link has changed is not relivant.
34332  *
34333  * Fork - LGPL
34334  * <script type="text/javascript">
34335  */
34336 /**
34337  * @class Roo.tree.TreeLoader
34338  * @extends Roo.util.Observable
34339  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34340  * nodes from a specified URL. The response must be a javascript Array definition
34341  * who's elements are node definition objects. eg:
34342  * <pre><code>
34343 {  success : true,
34344    data :      [
34345    
34346     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34347     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34348     ]
34349 }
34350
34351
34352 </code></pre>
34353  * <br><br>
34354  * The old style respose with just an array is still supported, but not recommended.
34355  * <br><br>
34356  *
34357  * A server request is sent, and child nodes are loaded only when a node is expanded.
34358  * The loading node's id is passed to the server under the parameter name "node" to
34359  * enable the server to produce the correct child nodes.
34360  * <br><br>
34361  * To pass extra parameters, an event handler may be attached to the "beforeload"
34362  * event, and the parameters specified in the TreeLoader's baseParams property:
34363  * <pre><code>
34364     myTreeLoader.on("beforeload", function(treeLoader, node) {
34365         this.baseParams.category = node.attributes.category;
34366     }, this);
34367 </code></pre><
34368  * This would pass an HTTP parameter called "category" to the server containing
34369  * the value of the Node's "category" attribute.
34370  * @constructor
34371  * Creates a new Treeloader.
34372  * @param {Object} config A config object containing config properties.
34373  */
34374 Roo.tree.TreeLoader = function(config){
34375     this.baseParams = {};
34376     this.requestMethod = "POST";
34377     Roo.apply(this, config);
34378
34379     this.addEvents({
34380     
34381         /**
34382          * @event beforeload
34383          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34384          * @param {Object} This TreeLoader object.
34385          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34386          * @param {Object} callback The callback function specified in the {@link #load} call.
34387          */
34388         beforeload : true,
34389         /**
34390          * @event load
34391          * Fires when the node has been successfuly loaded.
34392          * @param {Object} This TreeLoader object.
34393          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34394          * @param {Object} response The response object containing the data from the server.
34395          */
34396         load : true,
34397         /**
34398          * @event loadexception
34399          * Fires if the network request failed.
34400          * @param {Object} This TreeLoader object.
34401          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34402          * @param {Object} response The response object containing the data from the server.
34403          */
34404         loadexception : true,
34405         /**
34406          * @event create
34407          * Fires before a node is created, enabling you to return custom Node types 
34408          * @param {Object} This TreeLoader object.
34409          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34410          */
34411         create : true
34412     });
34413
34414     Roo.tree.TreeLoader.superclass.constructor.call(this);
34415 };
34416
34417 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34418     /**
34419     * @cfg {String} dataUrl The URL from which to request a Json string which
34420     * specifies an array of node definition object representing the child nodes
34421     * to be loaded.
34422     */
34423     /**
34424     * @cfg {String} requestMethod either GET or POST
34425     * defaults to POST (due to BC)
34426     * to be loaded.
34427     */
34428     /**
34429     * @cfg {Object} baseParams (optional) An object containing properties which
34430     * specify HTTP parameters to be passed to each request for child nodes.
34431     */
34432     /**
34433     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34434     * created by this loader. If the attributes sent by the server have an attribute in this object,
34435     * they take priority.
34436     */
34437     /**
34438     * @cfg {Object} uiProviders (optional) An object containing properties which
34439     * 
34440     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34441     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34442     * <i>uiProvider</i> attribute of a returned child node is a string rather
34443     * than a reference to a TreeNodeUI implementation, this that string value
34444     * is used as a property name in the uiProviders object. You can define the provider named
34445     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34446     */
34447     uiProviders : {},
34448
34449     /**
34450     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34451     * child nodes before loading.
34452     */
34453     clearOnLoad : true,
34454
34455     /**
34456     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34457     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34458     * Grid query { data : [ .....] }
34459     */
34460     
34461     root : false,
34462      /**
34463     * @cfg {String} queryParam (optional) 
34464     * Name of the query as it will be passed on the querystring (defaults to 'node')
34465     * eg. the request will be ?node=[id]
34466     */
34467     
34468     
34469     queryParam: false,
34470     
34471     /**
34472      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34473      * This is called automatically when a node is expanded, but may be used to reload
34474      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34475      * @param {Roo.tree.TreeNode} node
34476      * @param {Function} callback
34477      */
34478     load : function(node, callback){
34479         if(this.clearOnLoad){
34480             while(node.firstChild){
34481                 node.removeChild(node.firstChild);
34482             }
34483         }
34484         if(node.attributes.children){ // preloaded json children
34485             var cs = node.attributes.children;
34486             for(var i = 0, len = cs.length; i < len; i++){
34487                 node.appendChild(this.createNode(cs[i]));
34488             }
34489             if(typeof callback == "function"){
34490                 callback();
34491             }
34492         }else if(this.dataUrl){
34493             this.requestData(node, callback);
34494         }
34495     },
34496
34497     getParams: function(node){
34498         var buf = [], bp = this.baseParams;
34499         for(var key in bp){
34500             if(typeof bp[key] != "function"){
34501                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34502             }
34503         }
34504         var n = this.queryParam === false ? 'node' : this.queryParam;
34505         buf.push(n + "=", encodeURIComponent(node.id));
34506         return buf.join("");
34507     },
34508
34509     requestData : function(node, callback){
34510         if(this.fireEvent("beforeload", this, node, callback) !== false){
34511             this.transId = Roo.Ajax.request({
34512                 method:this.requestMethod,
34513                 url: this.dataUrl||this.url,
34514                 success: this.handleResponse,
34515                 failure: this.handleFailure,
34516                 scope: this,
34517                 argument: {callback: callback, node: node},
34518                 params: this.getParams(node)
34519             });
34520         }else{
34521             // if the load is cancelled, make sure we notify
34522             // the node that we are done
34523             if(typeof callback == "function"){
34524                 callback();
34525             }
34526         }
34527     },
34528
34529     isLoading : function(){
34530         return this.transId ? true : false;
34531     },
34532
34533     abort : function(){
34534         if(this.isLoading()){
34535             Roo.Ajax.abort(this.transId);
34536         }
34537     },
34538
34539     // private
34540     createNode : function(attr)
34541     {
34542         // apply baseAttrs, nice idea Corey!
34543         if(this.baseAttrs){
34544             Roo.applyIf(attr, this.baseAttrs);
34545         }
34546         if(this.applyLoader !== false){
34547             attr.loader = this;
34548         }
34549         // uiProvider = depreciated..
34550         
34551         if(typeof(attr.uiProvider) == 'string'){
34552            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34553                 /**  eval:var:attr */ eval(attr.uiProvider);
34554         }
34555         if(typeof(this.uiProviders['default']) != 'undefined') {
34556             attr.uiProvider = this.uiProviders['default'];
34557         }
34558         
34559         this.fireEvent('create', this, attr);
34560         
34561         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34562         return(attr.leaf ?
34563                         new Roo.tree.TreeNode(attr) :
34564                         new Roo.tree.AsyncTreeNode(attr));
34565     },
34566
34567     processResponse : function(response, node, callback)
34568     {
34569         var json = response.responseText;
34570         try {
34571             
34572             var o = Roo.decode(json);
34573             
34574             if (this.root === false && typeof(o.success) != undefined) {
34575                 this.root = 'data'; // the default behaviour for list like data..
34576                 }
34577                 
34578             if (this.root !== false &&  !o.success) {
34579                 // it's a failure condition.
34580                 var a = response.argument;
34581                 this.fireEvent("loadexception", this, a.node, response);
34582                 Roo.log("Load failed - should have a handler really");
34583                 return;
34584             }
34585             
34586             
34587             
34588             if (this.root !== false) {
34589                  o = o[this.root];
34590             }
34591             
34592             for(var i = 0, len = o.length; i < len; i++){
34593                 var n = this.createNode(o[i]);
34594                 if(n){
34595                     node.appendChild(n);
34596                 }
34597             }
34598             if(typeof callback == "function"){
34599                 callback(this, node);
34600             }
34601         }catch(e){
34602             this.handleFailure(response);
34603         }
34604     },
34605
34606     handleResponse : function(response){
34607         this.transId = false;
34608         var a = response.argument;
34609         this.processResponse(response, a.node, a.callback);
34610         this.fireEvent("load", this, a.node, response);
34611     },
34612
34613     handleFailure : function(response)
34614     {
34615         // should handle failure better..
34616         this.transId = false;
34617         var a = response.argument;
34618         this.fireEvent("loadexception", this, a.node, response);
34619         if(typeof a.callback == "function"){
34620             a.callback(this, a.node);
34621         }
34622     }
34623 });/*
34624  * Based on:
34625  * Ext JS Library 1.1.1
34626  * Copyright(c) 2006-2007, Ext JS, LLC.
34627  *
34628  * Originally Released Under LGPL - original licence link has changed is not relivant.
34629  *
34630  * Fork - LGPL
34631  * <script type="text/javascript">
34632  */
34633
34634 /**
34635 * @class Roo.tree.TreeFilter
34636 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34637 * @param {TreePanel} tree
34638 * @param {Object} config (optional)
34639  */
34640 Roo.tree.TreeFilter = function(tree, config){
34641     this.tree = tree;
34642     this.filtered = {};
34643     Roo.apply(this, config);
34644 };
34645
34646 Roo.tree.TreeFilter.prototype = {
34647     clearBlank:false,
34648     reverse:false,
34649     autoClear:false,
34650     remove:false,
34651
34652      /**
34653      * Filter the data by a specific attribute.
34654      * @param {String/RegExp} value Either string that the attribute value
34655      * should start with or a RegExp to test against the attribute
34656      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34657      * @param {TreeNode} startNode (optional) The node to start the filter at.
34658      */
34659     filter : function(value, attr, startNode){
34660         attr = attr || "text";
34661         var f;
34662         if(typeof value == "string"){
34663             var vlen = value.length;
34664             // auto clear empty filter
34665             if(vlen == 0 && this.clearBlank){
34666                 this.clear();
34667                 return;
34668             }
34669             value = value.toLowerCase();
34670             f = function(n){
34671                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34672             };
34673         }else if(value.exec){ // regex?
34674             f = function(n){
34675                 return value.test(n.attributes[attr]);
34676             };
34677         }else{
34678             throw 'Illegal filter type, must be string or regex';
34679         }
34680         this.filterBy(f, null, startNode);
34681         },
34682
34683     /**
34684      * Filter by a function. The passed function will be called with each
34685      * node in the tree (or from the startNode). If the function returns true, the node is kept
34686      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34687      * @param {Function} fn The filter function
34688      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34689      */
34690     filterBy : function(fn, scope, startNode){
34691         startNode = startNode || this.tree.root;
34692         if(this.autoClear){
34693             this.clear();
34694         }
34695         var af = this.filtered, rv = this.reverse;
34696         var f = function(n){
34697             if(n == startNode){
34698                 return true;
34699             }
34700             if(af[n.id]){
34701                 return false;
34702             }
34703             var m = fn.call(scope || n, n);
34704             if(!m || rv){
34705                 af[n.id] = n;
34706                 n.ui.hide();
34707                 return false;
34708             }
34709             return true;
34710         };
34711         startNode.cascade(f);
34712         if(this.remove){
34713            for(var id in af){
34714                if(typeof id != "function"){
34715                    var n = af[id];
34716                    if(n && n.parentNode){
34717                        n.parentNode.removeChild(n);
34718                    }
34719                }
34720            }
34721         }
34722     },
34723
34724     /**
34725      * Clears the current filter. Note: with the "remove" option
34726      * set a filter cannot be cleared.
34727      */
34728     clear : function(){
34729         var t = this.tree;
34730         var af = this.filtered;
34731         for(var id in af){
34732             if(typeof id != "function"){
34733                 var n = af[id];
34734                 if(n){
34735                     n.ui.show();
34736                 }
34737             }
34738         }
34739         this.filtered = {};
34740     }
34741 };
34742 /*
34743  * Based on:
34744  * Ext JS Library 1.1.1
34745  * Copyright(c) 2006-2007, Ext JS, LLC.
34746  *
34747  * Originally Released Under LGPL - original licence link has changed is not relivant.
34748  *
34749  * Fork - LGPL
34750  * <script type="text/javascript">
34751  */
34752  
34753
34754 /**
34755  * @class Roo.tree.TreeSorter
34756  * Provides sorting of nodes in a TreePanel
34757  * 
34758  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34759  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34760  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34761  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34762  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34763  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34764  * @constructor
34765  * @param {TreePanel} tree
34766  * @param {Object} config
34767  */
34768 Roo.tree.TreeSorter = function(tree, config){
34769     Roo.apply(this, config);
34770     tree.on("beforechildrenrendered", this.doSort, this);
34771     tree.on("append", this.updateSort, this);
34772     tree.on("insert", this.updateSort, this);
34773     
34774     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34775     var p = this.property || "text";
34776     var sortType = this.sortType;
34777     var fs = this.folderSort;
34778     var cs = this.caseSensitive === true;
34779     var leafAttr = this.leafAttr || 'leaf';
34780
34781     this.sortFn = function(n1, n2){
34782         if(fs){
34783             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34784                 return 1;
34785             }
34786             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34787                 return -1;
34788             }
34789         }
34790         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34791         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34792         if(v1 < v2){
34793                         return dsc ? +1 : -1;
34794                 }else if(v1 > v2){
34795                         return dsc ? -1 : +1;
34796         }else{
34797                 return 0;
34798         }
34799     };
34800 };
34801
34802 Roo.tree.TreeSorter.prototype = {
34803     doSort : function(node){
34804         node.sort(this.sortFn);
34805     },
34806     
34807     compareNodes : function(n1, n2){
34808         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34809     },
34810     
34811     updateSort : function(tree, node){
34812         if(node.childrenRendered){
34813             this.doSort.defer(1, this, [node]);
34814         }
34815     }
34816 };/*
34817  * Based on:
34818  * Ext JS Library 1.1.1
34819  * Copyright(c) 2006-2007, Ext JS, LLC.
34820  *
34821  * Originally Released Under LGPL - original licence link has changed is not relivant.
34822  *
34823  * Fork - LGPL
34824  * <script type="text/javascript">
34825  */
34826
34827 if(Roo.dd.DropZone){
34828     
34829 Roo.tree.TreeDropZone = function(tree, config){
34830     this.allowParentInsert = false;
34831     this.allowContainerDrop = false;
34832     this.appendOnly = false;
34833     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34834     this.tree = tree;
34835     this.lastInsertClass = "x-tree-no-status";
34836     this.dragOverData = {};
34837 };
34838
34839 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34840     ddGroup : "TreeDD",
34841     scroll:  true,
34842     
34843     expandDelay : 1000,
34844     
34845     expandNode : function(node){
34846         if(node.hasChildNodes() && !node.isExpanded()){
34847             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34848         }
34849     },
34850     
34851     queueExpand : function(node){
34852         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34853     },
34854     
34855     cancelExpand : function(){
34856         if(this.expandProcId){
34857             clearTimeout(this.expandProcId);
34858             this.expandProcId = false;
34859         }
34860     },
34861     
34862     isValidDropPoint : function(n, pt, dd, e, data){
34863         if(!n || !data){ return false; }
34864         var targetNode = n.node;
34865         var dropNode = data.node;
34866         // default drop rules
34867         if(!(targetNode && targetNode.isTarget && pt)){
34868             return false;
34869         }
34870         if(pt == "append" && targetNode.allowChildren === false){
34871             return false;
34872         }
34873         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34874             return false;
34875         }
34876         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34877             return false;
34878         }
34879         // reuse the object
34880         var overEvent = this.dragOverData;
34881         overEvent.tree = this.tree;
34882         overEvent.target = targetNode;
34883         overEvent.data = data;
34884         overEvent.point = pt;
34885         overEvent.source = dd;
34886         overEvent.rawEvent = e;
34887         overEvent.dropNode = dropNode;
34888         overEvent.cancel = false;  
34889         var result = this.tree.fireEvent("nodedragover", overEvent);
34890         return overEvent.cancel === false && result !== false;
34891     },
34892     
34893     getDropPoint : function(e, n, dd)
34894     {
34895         var tn = n.node;
34896         if(tn.isRoot){
34897             return tn.allowChildren !== false ? "append" : false; // always append for root
34898         }
34899         var dragEl = n.ddel;
34900         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34901         var y = Roo.lib.Event.getPageY(e);
34902         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34903         
34904         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34905         var noAppend = tn.allowChildren === false;
34906         if(this.appendOnly || tn.parentNode.allowChildren === false){
34907             return noAppend ? false : "append";
34908         }
34909         var noBelow = false;
34910         if(!this.allowParentInsert){
34911             noBelow = tn.hasChildNodes() && tn.isExpanded();
34912         }
34913         var q = (b - t) / (noAppend ? 2 : 3);
34914         if(y >= t && y < (t + q)){
34915             return "above";
34916         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34917             return "below";
34918         }else{
34919             return "append";
34920         }
34921     },
34922     
34923     onNodeEnter : function(n, dd, e, data)
34924     {
34925         this.cancelExpand();
34926     },
34927     
34928     onNodeOver : function(n, dd, e, data)
34929     {
34930        
34931         var pt = this.getDropPoint(e, n, dd);
34932         var node = n.node;
34933         
34934         // auto node expand check
34935         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34936             this.queueExpand(node);
34937         }else if(pt != "append"){
34938             this.cancelExpand();
34939         }
34940         
34941         // set the insert point style on the target node
34942         var returnCls = this.dropNotAllowed;
34943         if(this.isValidDropPoint(n, pt, dd, e, data)){
34944            if(pt){
34945                var el = n.ddel;
34946                var cls;
34947                if(pt == "above"){
34948                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34949                    cls = "x-tree-drag-insert-above";
34950                }else if(pt == "below"){
34951                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34952                    cls = "x-tree-drag-insert-below";
34953                }else{
34954                    returnCls = "x-tree-drop-ok-append";
34955                    cls = "x-tree-drag-append";
34956                }
34957                if(this.lastInsertClass != cls){
34958                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34959                    this.lastInsertClass = cls;
34960                }
34961            }
34962        }
34963        return returnCls;
34964     },
34965     
34966     onNodeOut : function(n, dd, e, data){
34967         
34968         this.cancelExpand();
34969         this.removeDropIndicators(n);
34970     },
34971     
34972     onNodeDrop : function(n, dd, e, data){
34973         var point = this.getDropPoint(e, n, dd);
34974         var targetNode = n.node;
34975         targetNode.ui.startDrop();
34976         if(!this.isValidDropPoint(n, point, dd, e, data)){
34977             targetNode.ui.endDrop();
34978             return false;
34979         }
34980         // first try to find the drop node
34981         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34982         var dropEvent = {
34983             tree : this.tree,
34984             target: targetNode,
34985             data: data,
34986             point: point,
34987             source: dd,
34988             rawEvent: e,
34989             dropNode: dropNode,
34990             cancel: !dropNode   
34991         };
34992         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34993         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34994             targetNode.ui.endDrop();
34995             return false;
34996         }
34997         // allow target changing
34998         targetNode = dropEvent.target;
34999         if(point == "append" && !targetNode.isExpanded()){
35000             targetNode.expand(false, null, function(){
35001                 this.completeDrop(dropEvent);
35002             }.createDelegate(this));
35003         }else{
35004             this.completeDrop(dropEvent);
35005         }
35006         return true;
35007     },
35008     
35009     completeDrop : function(de){
35010         var ns = de.dropNode, p = de.point, t = de.target;
35011         if(!(ns instanceof Array)){
35012             ns = [ns];
35013         }
35014         var n;
35015         for(var i = 0, len = ns.length; i < len; i++){
35016             n = ns[i];
35017             if(p == "above"){
35018                 t.parentNode.insertBefore(n, t);
35019             }else if(p == "below"){
35020                 t.parentNode.insertBefore(n, t.nextSibling);
35021             }else{
35022                 t.appendChild(n);
35023             }
35024         }
35025         n.ui.focus();
35026         if(this.tree.hlDrop){
35027             n.ui.highlight();
35028         }
35029         t.ui.endDrop();
35030         this.tree.fireEvent("nodedrop", de);
35031     },
35032     
35033     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35034         if(this.tree.hlDrop){
35035             dropNode.ui.focus();
35036             dropNode.ui.highlight();
35037         }
35038         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35039     },
35040     
35041     getTree : function(){
35042         return this.tree;
35043     },
35044     
35045     removeDropIndicators : function(n){
35046         if(n && n.ddel){
35047             var el = n.ddel;
35048             Roo.fly(el).removeClass([
35049                     "x-tree-drag-insert-above",
35050                     "x-tree-drag-insert-below",
35051                     "x-tree-drag-append"]);
35052             this.lastInsertClass = "_noclass";
35053         }
35054     },
35055     
35056     beforeDragDrop : function(target, e, id){
35057         this.cancelExpand();
35058         return true;
35059     },
35060     
35061     afterRepair : function(data){
35062         if(data && Roo.enableFx){
35063             data.node.ui.highlight();
35064         }
35065         this.hideProxy();
35066     } 
35067     
35068 });
35069
35070 }
35071 /*
35072  * Based on:
35073  * Ext JS Library 1.1.1
35074  * Copyright(c) 2006-2007, Ext JS, LLC.
35075  *
35076  * Originally Released Under LGPL - original licence link has changed is not relivant.
35077  *
35078  * Fork - LGPL
35079  * <script type="text/javascript">
35080  */
35081  
35082
35083 if(Roo.dd.DragZone){
35084 Roo.tree.TreeDragZone = function(tree, config){
35085     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35086     this.tree = tree;
35087 };
35088
35089 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35090     ddGroup : "TreeDD",
35091    
35092     onBeforeDrag : function(data, e){
35093         var n = data.node;
35094         return n && n.draggable && !n.disabled;
35095     },
35096      
35097     
35098     onInitDrag : function(e){
35099         var data = this.dragData;
35100         this.tree.getSelectionModel().select(data.node);
35101         this.proxy.update("");
35102         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35103         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35104     },
35105     
35106     getRepairXY : function(e, data){
35107         return data.node.ui.getDDRepairXY();
35108     },
35109     
35110     onEndDrag : function(data, e){
35111         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35112         
35113         
35114     },
35115     
35116     onValidDrop : function(dd, e, id){
35117         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35118         this.hideProxy();
35119     },
35120     
35121     beforeInvalidDrop : function(e, id){
35122         // this scrolls the original position back into view
35123         var sm = this.tree.getSelectionModel();
35124         sm.clearSelections();
35125         sm.select(this.dragData.node);
35126     }
35127 });
35128 }/*
35129  * Based on:
35130  * Ext JS Library 1.1.1
35131  * Copyright(c) 2006-2007, Ext JS, LLC.
35132  *
35133  * Originally Released Under LGPL - original licence link has changed is not relivant.
35134  *
35135  * Fork - LGPL
35136  * <script type="text/javascript">
35137  */
35138 /**
35139  * @class Roo.tree.TreeEditor
35140  * @extends Roo.Editor
35141  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35142  * as the editor field.
35143  * @constructor
35144  * @param {Object} config (used to be the tree panel.)
35145  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35146  * 
35147  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35148  * @cfg {Roo.form.TextField|Object} field The field configuration
35149  *
35150  * 
35151  */
35152 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35153     var tree = config;
35154     var field;
35155     if (oldconfig) { // old style..
35156         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35157     } else {
35158         // new style..
35159         tree = config.tree;
35160         config.field = config.field  || {};
35161         config.field.xtype = 'TextField';
35162         field = Roo.factory(config.field, Roo.form);
35163     }
35164     config = config || {};
35165     
35166     
35167     this.addEvents({
35168         /**
35169          * @event beforenodeedit
35170          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35171          * false from the handler of this event.
35172          * @param {Editor} this
35173          * @param {Roo.tree.Node} node 
35174          */
35175         "beforenodeedit" : true
35176     });
35177     
35178     //Roo.log(config);
35179     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35180
35181     this.tree = tree;
35182
35183     tree.on('beforeclick', this.beforeNodeClick, this);
35184     tree.getTreeEl().on('mousedown', this.hide, this);
35185     this.on('complete', this.updateNode, this);
35186     this.on('beforestartedit', this.fitToTree, this);
35187     this.on('startedit', this.bindScroll, this, {delay:10});
35188     this.on('specialkey', this.onSpecialKey, this);
35189 };
35190
35191 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35192     /**
35193      * @cfg {String} alignment
35194      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35195      */
35196     alignment: "l-l",
35197     // inherit
35198     autoSize: false,
35199     /**
35200      * @cfg {Boolean} hideEl
35201      * True to hide the bound element while the editor is displayed (defaults to false)
35202      */
35203     hideEl : false,
35204     /**
35205      * @cfg {String} cls
35206      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35207      */
35208     cls: "x-small-editor x-tree-editor",
35209     /**
35210      * @cfg {Boolean} shim
35211      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35212      */
35213     shim:false,
35214     // inherit
35215     shadow:"frame",
35216     /**
35217      * @cfg {Number} maxWidth
35218      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35219      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35220      * scroll and client offsets into account prior to each edit.
35221      */
35222     maxWidth: 250,
35223
35224     editDelay : 350,
35225
35226     // private
35227     fitToTree : function(ed, el){
35228         var td = this.tree.getTreeEl().dom, nd = el.dom;
35229         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35230             td.scrollLeft = nd.offsetLeft;
35231         }
35232         var w = Math.min(
35233                 this.maxWidth,
35234                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35235         this.setSize(w, '');
35236         
35237         return this.fireEvent('beforenodeedit', this, this.editNode);
35238         
35239     },
35240
35241     // private
35242     triggerEdit : function(node){
35243         this.completeEdit();
35244         this.editNode = node;
35245         this.startEdit(node.ui.textNode, node.text);
35246     },
35247
35248     // private
35249     bindScroll : function(){
35250         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35251     },
35252
35253     // private
35254     beforeNodeClick : function(node, e){
35255         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35256         this.lastClick = new Date();
35257         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35258             e.stopEvent();
35259             this.triggerEdit(node);
35260             return false;
35261         }
35262         return true;
35263     },
35264
35265     // private
35266     updateNode : function(ed, value){
35267         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35268         this.editNode.setText(value);
35269     },
35270
35271     // private
35272     onHide : function(){
35273         Roo.tree.TreeEditor.superclass.onHide.call(this);
35274         if(this.editNode){
35275             this.editNode.ui.focus();
35276         }
35277     },
35278
35279     // private
35280     onSpecialKey : function(field, e){
35281         var k = e.getKey();
35282         if(k == e.ESC){
35283             e.stopEvent();
35284             this.cancelEdit();
35285         }else if(k == e.ENTER && !e.hasModifier()){
35286             e.stopEvent();
35287             this.completeEdit();
35288         }
35289     }
35290 });//<Script type="text/javascript">
35291 /*
35292  * Based on:
35293  * Ext JS Library 1.1.1
35294  * Copyright(c) 2006-2007, Ext JS, LLC.
35295  *
35296  * Originally Released Under LGPL - original licence link has changed is not relivant.
35297  *
35298  * Fork - LGPL
35299  * <script type="text/javascript">
35300  */
35301  
35302 /**
35303  * Not documented??? - probably should be...
35304  */
35305
35306 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35307     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35308     
35309     renderElements : function(n, a, targetNode, bulkRender){
35310         //consel.log("renderElements?");
35311         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35312
35313         var t = n.getOwnerTree();
35314         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35315         
35316         var cols = t.columns;
35317         var bw = t.borderWidth;
35318         var c = cols[0];
35319         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35320          var cb = typeof a.checked == "boolean";
35321         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35322         var colcls = 'x-t-' + tid + '-c0';
35323         var buf = [
35324             '<li class="x-tree-node">',
35325             
35326                 
35327                 '<div class="x-tree-node-el ', a.cls,'">',
35328                     // extran...
35329                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35330                 
35331                 
35332                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35333                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35334                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35335                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35336                            (a.iconCls ? ' '+a.iconCls : ''),
35337                            '" unselectable="on" />',
35338                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35339                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35340                              
35341                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35342                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35343                             '<span unselectable="on" qtip="' + tx + '">',
35344                              tx,
35345                              '</span></a>' ,
35346                     '</div>',
35347                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35348                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35349                  ];
35350         for(var i = 1, len = cols.length; i < len; i++){
35351             c = cols[i];
35352             colcls = 'x-t-' + tid + '-c' +i;
35353             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35354             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35355                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35356                       "</div>");
35357          }
35358          
35359          buf.push(
35360             '</a>',
35361             '<div class="x-clear"></div></div>',
35362             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35363             "</li>");
35364         
35365         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35366             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35367                                 n.nextSibling.ui.getEl(), buf.join(""));
35368         }else{
35369             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35370         }
35371         var el = this.wrap.firstChild;
35372         this.elRow = el;
35373         this.elNode = el.firstChild;
35374         this.ranchor = el.childNodes[1];
35375         this.ctNode = this.wrap.childNodes[1];
35376         var cs = el.firstChild.childNodes;
35377         this.indentNode = cs[0];
35378         this.ecNode = cs[1];
35379         this.iconNode = cs[2];
35380         var index = 3;
35381         if(cb){
35382             this.checkbox = cs[3];
35383             index++;
35384         }
35385         this.anchor = cs[index];
35386         
35387         this.textNode = cs[index].firstChild;
35388         
35389         //el.on("click", this.onClick, this);
35390         //el.on("dblclick", this.onDblClick, this);
35391         
35392         
35393        // console.log(this);
35394     },
35395     initEvents : function(){
35396         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35397         
35398             
35399         var a = this.ranchor;
35400
35401         var el = Roo.get(a);
35402
35403         if(Roo.isOpera){ // opera render bug ignores the CSS
35404             el.setStyle("text-decoration", "none");
35405         }
35406
35407         el.on("click", this.onClick, this);
35408         el.on("dblclick", this.onDblClick, this);
35409         el.on("contextmenu", this.onContextMenu, this);
35410         
35411     },
35412     
35413     /*onSelectedChange : function(state){
35414         if(state){
35415             this.focus();
35416             this.addClass("x-tree-selected");
35417         }else{
35418             //this.blur();
35419             this.removeClass("x-tree-selected");
35420         }
35421     },*/
35422     addClass : function(cls){
35423         if(this.elRow){
35424             Roo.fly(this.elRow).addClass(cls);
35425         }
35426         
35427     },
35428     
35429     
35430     removeClass : function(cls){
35431         if(this.elRow){
35432             Roo.fly(this.elRow).removeClass(cls);
35433         }
35434     }
35435
35436     
35437     
35438 });//<Script type="text/javascript">
35439
35440 /*
35441  * Based on:
35442  * Ext JS Library 1.1.1
35443  * Copyright(c) 2006-2007, Ext JS, LLC.
35444  *
35445  * Originally Released Under LGPL - original licence link has changed is not relivant.
35446  *
35447  * Fork - LGPL
35448  * <script type="text/javascript">
35449  */
35450  
35451
35452 /**
35453  * @class Roo.tree.ColumnTree
35454  * @extends Roo.data.TreePanel
35455  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35456  * @cfg {int} borderWidth  compined right/left border allowance
35457  * @constructor
35458  * @param {String/HTMLElement/Element} el The container element
35459  * @param {Object} config
35460  */
35461 Roo.tree.ColumnTree =  function(el, config)
35462 {
35463    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35464    this.addEvents({
35465         /**
35466         * @event resize
35467         * Fire this event on a container when it resizes
35468         * @param {int} w Width
35469         * @param {int} h Height
35470         */
35471        "resize" : true
35472     });
35473     this.on('resize', this.onResize, this);
35474 };
35475
35476 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35477     //lines:false,
35478     
35479     
35480     borderWidth: Roo.isBorderBox ? 0 : 2, 
35481     headEls : false,
35482     
35483     render : function(){
35484         // add the header.....
35485        
35486         Roo.tree.ColumnTree.superclass.render.apply(this);
35487         
35488         this.el.addClass('x-column-tree');
35489         
35490         this.headers = this.el.createChild(
35491             {cls:'x-tree-headers'},this.innerCt.dom);
35492    
35493         var cols = this.columns, c;
35494         var totalWidth = 0;
35495         this.headEls = [];
35496         var  len = cols.length;
35497         for(var i = 0; i < len; i++){
35498              c = cols[i];
35499              totalWidth += c.width;
35500             this.headEls.push(this.headers.createChild({
35501                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35502                  cn: {
35503                      cls:'x-tree-hd-text',
35504                      html: c.header
35505                  },
35506                  style:'width:'+(c.width-this.borderWidth)+'px;'
35507              }));
35508         }
35509         this.headers.createChild({cls:'x-clear'});
35510         // prevent floats from wrapping when clipped
35511         this.headers.setWidth(totalWidth);
35512         //this.innerCt.setWidth(totalWidth);
35513         this.innerCt.setStyle({ overflow: 'auto' });
35514         this.onResize(this.width, this.height);
35515              
35516         
35517     },
35518     onResize : function(w,h)
35519     {
35520         this.height = h;
35521         this.width = w;
35522         // resize cols..
35523         this.innerCt.setWidth(this.width);
35524         this.innerCt.setHeight(this.height-20);
35525         
35526         // headers...
35527         var cols = this.columns, c;
35528         var totalWidth = 0;
35529         var expEl = false;
35530         var len = cols.length;
35531         for(var i = 0; i < len; i++){
35532             c = cols[i];
35533             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35534                 // it's the expander..
35535                 expEl  = this.headEls[i];
35536                 continue;
35537             }
35538             totalWidth += c.width;
35539             
35540         }
35541         if (expEl) {
35542             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35543         }
35544         this.headers.setWidth(w-20);
35545
35546         
35547         
35548         
35549     }
35550 });
35551 /*
35552  * Based on:
35553  * Ext JS Library 1.1.1
35554  * Copyright(c) 2006-2007, Ext JS, LLC.
35555  *
35556  * Originally Released Under LGPL - original licence link has changed is not relivant.
35557  *
35558  * Fork - LGPL
35559  * <script type="text/javascript">
35560  */
35561  
35562 /**
35563  * @class Roo.menu.Menu
35564  * @extends Roo.util.Observable
35565  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35566  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35567  * @constructor
35568  * Creates a new Menu
35569  * @param {Object} config Configuration options
35570  */
35571 Roo.menu.Menu = function(config){
35572     Roo.apply(this, config);
35573     this.id = this.id || Roo.id();
35574     this.addEvents({
35575         /**
35576          * @event beforeshow
35577          * Fires before this menu is displayed
35578          * @param {Roo.menu.Menu} this
35579          */
35580         beforeshow : true,
35581         /**
35582          * @event beforehide
35583          * Fires before this menu is hidden
35584          * @param {Roo.menu.Menu} this
35585          */
35586         beforehide : true,
35587         /**
35588          * @event show
35589          * Fires after this menu is displayed
35590          * @param {Roo.menu.Menu} this
35591          */
35592         show : true,
35593         /**
35594          * @event hide
35595          * Fires after this menu is hidden
35596          * @param {Roo.menu.Menu} this
35597          */
35598         hide : true,
35599         /**
35600          * @event click
35601          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35602          * @param {Roo.menu.Menu} this
35603          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35604          * @param {Roo.EventObject} e
35605          */
35606         click : true,
35607         /**
35608          * @event mouseover
35609          * Fires when the mouse is hovering over this menu
35610          * @param {Roo.menu.Menu} this
35611          * @param {Roo.EventObject} e
35612          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35613          */
35614         mouseover : true,
35615         /**
35616          * @event mouseout
35617          * Fires when the mouse exits this menu
35618          * @param {Roo.menu.Menu} this
35619          * @param {Roo.EventObject} e
35620          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35621          */
35622         mouseout : true,
35623         /**
35624          * @event itemclick
35625          * Fires when a menu item contained in this menu is clicked
35626          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35627          * @param {Roo.EventObject} e
35628          */
35629         itemclick: true
35630     });
35631     if (this.registerMenu) {
35632         Roo.menu.MenuMgr.register(this);
35633     }
35634     
35635     var mis = this.items;
35636     this.items = new Roo.util.MixedCollection();
35637     if(mis){
35638         this.add.apply(this, mis);
35639     }
35640 };
35641
35642 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35643     /**
35644      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35645      */
35646     minWidth : 120,
35647     /**
35648      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35649      * for bottom-right shadow (defaults to "sides")
35650      */
35651     shadow : "sides",
35652     /**
35653      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35654      * this menu (defaults to "tl-tr?")
35655      */
35656     subMenuAlign : "tl-tr?",
35657     /**
35658      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35659      * relative to its element of origin (defaults to "tl-bl?")
35660      */
35661     defaultAlign : "tl-bl?",
35662     /**
35663      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35664      */
35665     allowOtherMenus : false,
35666     /**
35667      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35668      */
35669     registerMenu : true,
35670
35671     hidden:true,
35672
35673     // private
35674     render : function(){
35675         if(this.el){
35676             return;
35677         }
35678         var el = this.el = new Roo.Layer({
35679             cls: "x-menu",
35680             shadow:this.shadow,
35681             constrain: false,
35682             parentEl: this.parentEl || document.body,
35683             zindex:15000
35684         });
35685
35686         this.keyNav = new Roo.menu.MenuNav(this);
35687
35688         if(this.plain){
35689             el.addClass("x-menu-plain");
35690         }
35691         if(this.cls){
35692             el.addClass(this.cls);
35693         }
35694         // generic focus element
35695         this.focusEl = el.createChild({
35696             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35697         });
35698         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35699         //disabling touch- as it's causing issues ..
35700         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35701         ul.on('click'   , this.onClick, this);
35702         
35703         
35704         ul.on("mouseover", this.onMouseOver, this);
35705         ul.on("mouseout", this.onMouseOut, this);
35706         this.items.each(function(item){
35707             if (item.hidden) {
35708                 return;
35709             }
35710             
35711             var li = document.createElement("li");
35712             li.className = "x-menu-list-item";
35713             ul.dom.appendChild(li);
35714             item.render(li, this);
35715         }, this);
35716         this.ul = ul;
35717         this.autoWidth();
35718     },
35719
35720     // private
35721     autoWidth : function(){
35722         var el = this.el, ul = this.ul;
35723         if(!el){
35724             return;
35725         }
35726         var w = this.width;
35727         if(w){
35728             el.setWidth(w);
35729         }else if(Roo.isIE){
35730             el.setWidth(this.minWidth);
35731             var t = el.dom.offsetWidth; // force recalc
35732             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35733         }
35734     },
35735
35736     // private
35737     delayAutoWidth : function(){
35738         if(this.rendered){
35739             if(!this.awTask){
35740                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35741             }
35742             this.awTask.delay(20);
35743         }
35744     },
35745
35746     // private
35747     findTargetItem : function(e){
35748         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35749         if(t && t.menuItemId){
35750             return this.items.get(t.menuItemId);
35751         }
35752     },
35753
35754     // private
35755     onClick : function(e){
35756         Roo.log("menu.onClick");
35757         var t = this.findTargetItem(e);
35758         if(!t){
35759             return;
35760         }
35761         Roo.log(e);
35762         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35763             if(t == this.activeItem && t.shouldDeactivate(e)){
35764                 this.activeItem.deactivate();
35765                 delete this.activeItem;
35766                 return;
35767             }
35768             if(t.canActivate){
35769                 this.setActiveItem(t, true);
35770             }
35771             return;
35772             
35773             
35774         }
35775         
35776         t.onClick(e);
35777         this.fireEvent("click", this, t, e);
35778     },
35779
35780     // private
35781     setActiveItem : function(item, autoExpand){
35782         if(item != this.activeItem){
35783             if(this.activeItem){
35784                 this.activeItem.deactivate();
35785             }
35786             this.activeItem = item;
35787             item.activate(autoExpand);
35788         }else if(autoExpand){
35789             item.expandMenu();
35790         }
35791     },
35792
35793     // private
35794     tryActivate : function(start, step){
35795         var items = this.items;
35796         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35797             var item = items.get(i);
35798             if(!item.disabled && item.canActivate){
35799                 this.setActiveItem(item, false);
35800                 return item;
35801             }
35802         }
35803         return false;
35804     },
35805
35806     // private
35807     onMouseOver : function(e){
35808         var t;
35809         if(t = this.findTargetItem(e)){
35810             if(t.canActivate && !t.disabled){
35811                 this.setActiveItem(t, true);
35812             }
35813         }
35814         this.fireEvent("mouseover", this, e, t);
35815     },
35816
35817     // private
35818     onMouseOut : function(e){
35819         var t;
35820         if(t = this.findTargetItem(e)){
35821             if(t == this.activeItem && t.shouldDeactivate(e)){
35822                 this.activeItem.deactivate();
35823                 delete this.activeItem;
35824             }
35825         }
35826         this.fireEvent("mouseout", this, e, t);
35827     },
35828
35829     /**
35830      * Read-only.  Returns true if the menu is currently displayed, else false.
35831      * @type Boolean
35832      */
35833     isVisible : function(){
35834         return this.el && !this.hidden;
35835     },
35836
35837     /**
35838      * Displays this menu relative to another element
35839      * @param {String/HTMLElement/Roo.Element} element The element to align to
35840      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35841      * the element (defaults to this.defaultAlign)
35842      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35843      */
35844     show : function(el, pos, parentMenu){
35845         this.parentMenu = parentMenu;
35846         if(!this.el){
35847             this.render();
35848         }
35849         this.fireEvent("beforeshow", this);
35850         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35851     },
35852
35853     /**
35854      * Displays this menu at a specific xy position
35855      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35856      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35857      */
35858     showAt : function(xy, parentMenu, /* private: */_e){
35859         this.parentMenu = parentMenu;
35860         if(!this.el){
35861             this.render();
35862         }
35863         if(_e !== false){
35864             this.fireEvent("beforeshow", this);
35865             xy = this.el.adjustForConstraints(xy);
35866         }
35867         this.el.setXY(xy);
35868         this.el.show();
35869         this.hidden = false;
35870         this.focus();
35871         this.fireEvent("show", this);
35872     },
35873
35874     focus : function(){
35875         if(!this.hidden){
35876             this.doFocus.defer(50, this);
35877         }
35878     },
35879
35880     doFocus : function(){
35881         if(!this.hidden){
35882             this.focusEl.focus();
35883         }
35884     },
35885
35886     /**
35887      * Hides this menu and optionally all parent menus
35888      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35889      */
35890     hide : function(deep){
35891         if(this.el && this.isVisible()){
35892             this.fireEvent("beforehide", this);
35893             if(this.activeItem){
35894                 this.activeItem.deactivate();
35895                 this.activeItem = null;
35896             }
35897             this.el.hide();
35898             this.hidden = true;
35899             this.fireEvent("hide", this);
35900         }
35901         if(deep === true && this.parentMenu){
35902             this.parentMenu.hide(true);
35903         }
35904     },
35905
35906     /**
35907      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35908      * Any of the following are valid:
35909      * <ul>
35910      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35911      * <li>An HTMLElement object which will be converted to a menu item</li>
35912      * <li>A menu item config object that will be created as a new menu item</li>
35913      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35914      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35915      * </ul>
35916      * Usage:
35917      * <pre><code>
35918 // Create the menu
35919 var menu = new Roo.menu.Menu();
35920
35921 // Create a menu item to add by reference
35922 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35923
35924 // Add a bunch of items at once using different methods.
35925 // Only the last item added will be returned.
35926 var item = menu.add(
35927     menuItem,                // add existing item by ref
35928     'Dynamic Item',          // new TextItem
35929     '-',                     // new separator
35930     { text: 'Config Item' }  // new item by config
35931 );
35932 </code></pre>
35933      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35934      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35935      */
35936     add : function(){
35937         var a = arguments, l = a.length, item;
35938         for(var i = 0; i < l; i++){
35939             var el = a[i];
35940             if ((typeof(el) == "object") && el.xtype && el.xns) {
35941                 el = Roo.factory(el, Roo.menu);
35942             }
35943             
35944             if(el.render){ // some kind of Item
35945                 item = this.addItem(el);
35946             }else if(typeof el == "string"){ // string
35947                 if(el == "separator" || el == "-"){
35948                     item = this.addSeparator();
35949                 }else{
35950                     item = this.addText(el);
35951                 }
35952             }else if(el.tagName || el.el){ // element
35953                 item = this.addElement(el);
35954             }else if(typeof el == "object"){ // must be menu item config?
35955                 item = this.addMenuItem(el);
35956             }
35957         }
35958         return item;
35959     },
35960
35961     /**
35962      * Returns this menu's underlying {@link Roo.Element} object
35963      * @return {Roo.Element} The element
35964      */
35965     getEl : function(){
35966         if(!this.el){
35967             this.render();
35968         }
35969         return this.el;
35970     },
35971
35972     /**
35973      * Adds a separator bar to the menu
35974      * @return {Roo.menu.Item} The menu item that was added
35975      */
35976     addSeparator : function(){
35977         return this.addItem(new Roo.menu.Separator());
35978     },
35979
35980     /**
35981      * Adds an {@link Roo.Element} object to the menu
35982      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35983      * @return {Roo.menu.Item} The menu item that was added
35984      */
35985     addElement : function(el){
35986         return this.addItem(new Roo.menu.BaseItem(el));
35987     },
35988
35989     /**
35990      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35991      * @param {Roo.menu.Item} item The menu item to add
35992      * @return {Roo.menu.Item} The menu item that was added
35993      */
35994     addItem : function(item){
35995         this.items.add(item);
35996         if(this.ul){
35997             var li = document.createElement("li");
35998             li.className = "x-menu-list-item";
35999             this.ul.dom.appendChild(li);
36000             item.render(li, this);
36001             this.delayAutoWidth();
36002         }
36003         return item;
36004     },
36005
36006     /**
36007      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
36008      * @param {Object} config A MenuItem config object
36009      * @return {Roo.menu.Item} The menu item that was added
36010      */
36011     addMenuItem : function(config){
36012         if(!(config instanceof Roo.menu.Item)){
36013             if(typeof config.checked == "boolean"){ // must be check menu item config?
36014                 config = new Roo.menu.CheckItem(config);
36015             }else{
36016                 config = new Roo.menu.Item(config);
36017             }
36018         }
36019         return this.addItem(config);
36020     },
36021
36022     /**
36023      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36024      * @param {String} text The text to display in the menu item
36025      * @return {Roo.menu.Item} The menu item that was added
36026      */
36027     addText : function(text){
36028         return this.addItem(new Roo.menu.TextItem({ text : text }));
36029     },
36030
36031     /**
36032      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36033      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36034      * @param {Roo.menu.Item} item The menu item to add
36035      * @return {Roo.menu.Item} The menu item that was added
36036      */
36037     insert : function(index, item){
36038         this.items.insert(index, item);
36039         if(this.ul){
36040             var li = document.createElement("li");
36041             li.className = "x-menu-list-item";
36042             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36043             item.render(li, this);
36044             this.delayAutoWidth();
36045         }
36046         return item;
36047     },
36048
36049     /**
36050      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36051      * @param {Roo.menu.Item} item The menu item to remove
36052      */
36053     remove : function(item){
36054         this.items.removeKey(item.id);
36055         item.destroy();
36056     },
36057
36058     /**
36059      * Removes and destroys all items in the menu
36060      */
36061     removeAll : function(){
36062         var f;
36063         while(f = this.items.first()){
36064             this.remove(f);
36065         }
36066     }
36067 });
36068
36069 // MenuNav is a private utility class used internally by the Menu
36070 Roo.menu.MenuNav = function(menu){
36071     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36072     this.scope = this.menu = menu;
36073 };
36074
36075 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36076     doRelay : function(e, h){
36077         var k = e.getKey();
36078         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36079             this.menu.tryActivate(0, 1);
36080             return false;
36081         }
36082         return h.call(this.scope || this, e, this.menu);
36083     },
36084
36085     up : function(e, m){
36086         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36087             m.tryActivate(m.items.length-1, -1);
36088         }
36089     },
36090
36091     down : function(e, m){
36092         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36093             m.tryActivate(0, 1);
36094         }
36095     },
36096
36097     right : function(e, m){
36098         if(m.activeItem){
36099             m.activeItem.expandMenu(true);
36100         }
36101     },
36102
36103     left : function(e, m){
36104         m.hide();
36105         if(m.parentMenu && m.parentMenu.activeItem){
36106             m.parentMenu.activeItem.activate();
36107         }
36108     },
36109
36110     enter : function(e, m){
36111         if(m.activeItem){
36112             e.stopPropagation();
36113             m.activeItem.onClick(e);
36114             m.fireEvent("click", this, m.activeItem);
36115             return true;
36116         }
36117     }
36118 });/*
36119  * Based on:
36120  * Ext JS Library 1.1.1
36121  * Copyright(c) 2006-2007, Ext JS, LLC.
36122  *
36123  * Originally Released Under LGPL - original licence link has changed is not relivant.
36124  *
36125  * Fork - LGPL
36126  * <script type="text/javascript">
36127  */
36128  
36129 /**
36130  * @class Roo.menu.MenuMgr
36131  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36132  * @singleton
36133  */
36134 Roo.menu.MenuMgr = function(){
36135    var menus, active, groups = {}, attached = false, lastShow = new Date();
36136
36137    // private - called when first menu is created
36138    function init(){
36139        menus = {};
36140        active = new Roo.util.MixedCollection();
36141        Roo.get(document).addKeyListener(27, function(){
36142            if(active.length > 0){
36143                hideAll();
36144            }
36145        });
36146    }
36147
36148    // private
36149    function hideAll(){
36150        if(active && active.length > 0){
36151            var c = active.clone();
36152            c.each(function(m){
36153                m.hide();
36154            });
36155        }
36156    }
36157
36158    // private
36159    function onHide(m){
36160        active.remove(m);
36161        if(active.length < 1){
36162            Roo.get(document).un("mousedown", onMouseDown);
36163            attached = false;
36164        }
36165    }
36166
36167    // private
36168    function onShow(m){
36169        var last = active.last();
36170        lastShow = new Date();
36171        active.add(m);
36172        if(!attached){
36173            Roo.get(document).on("mousedown", onMouseDown);
36174            attached = true;
36175        }
36176        if(m.parentMenu){
36177           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36178           m.parentMenu.activeChild = m;
36179        }else if(last && last.isVisible()){
36180           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36181        }
36182    }
36183
36184    // private
36185    function onBeforeHide(m){
36186        if(m.activeChild){
36187            m.activeChild.hide();
36188        }
36189        if(m.autoHideTimer){
36190            clearTimeout(m.autoHideTimer);
36191            delete m.autoHideTimer;
36192        }
36193    }
36194
36195    // private
36196    function onBeforeShow(m){
36197        var pm = m.parentMenu;
36198        if(!pm && !m.allowOtherMenus){
36199            hideAll();
36200        }else if(pm && pm.activeChild && active != m){
36201            pm.activeChild.hide();
36202        }
36203    }
36204
36205    // private
36206    function onMouseDown(e){
36207        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36208            hideAll();
36209        }
36210    }
36211
36212    // private
36213    function onBeforeCheck(mi, state){
36214        if(state){
36215            var g = groups[mi.group];
36216            for(var i = 0, l = g.length; i < l; i++){
36217                if(g[i] != mi){
36218                    g[i].setChecked(false);
36219                }
36220            }
36221        }
36222    }
36223
36224    return {
36225
36226        /**
36227         * Hides all menus that are currently visible
36228         */
36229        hideAll : function(){
36230             hideAll();  
36231        },
36232
36233        // private
36234        register : function(menu){
36235            if(!menus){
36236                init();
36237            }
36238            menus[menu.id] = menu;
36239            menu.on("beforehide", onBeforeHide);
36240            menu.on("hide", onHide);
36241            menu.on("beforeshow", onBeforeShow);
36242            menu.on("show", onShow);
36243            var g = menu.group;
36244            if(g && menu.events["checkchange"]){
36245                if(!groups[g]){
36246                    groups[g] = [];
36247                }
36248                groups[g].push(menu);
36249                menu.on("checkchange", onCheck);
36250            }
36251        },
36252
36253         /**
36254          * Returns a {@link Roo.menu.Menu} object
36255          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36256          * be used to generate and return a new Menu instance.
36257          */
36258        get : function(menu){
36259            if(typeof menu == "string"){ // menu id
36260                return menus[menu];
36261            }else if(menu.events){  // menu instance
36262                return menu;
36263            }else if(typeof menu.length == 'number'){ // array of menu items?
36264                return new Roo.menu.Menu({items:menu});
36265            }else{ // otherwise, must be a config
36266                return new Roo.menu.Menu(menu);
36267            }
36268        },
36269
36270        // private
36271        unregister : function(menu){
36272            delete menus[menu.id];
36273            menu.un("beforehide", onBeforeHide);
36274            menu.un("hide", onHide);
36275            menu.un("beforeshow", onBeforeShow);
36276            menu.un("show", onShow);
36277            var g = menu.group;
36278            if(g && menu.events["checkchange"]){
36279                groups[g].remove(menu);
36280                menu.un("checkchange", onCheck);
36281            }
36282        },
36283
36284        // private
36285        registerCheckable : function(menuItem){
36286            var g = menuItem.group;
36287            if(g){
36288                if(!groups[g]){
36289                    groups[g] = [];
36290                }
36291                groups[g].push(menuItem);
36292                menuItem.on("beforecheckchange", onBeforeCheck);
36293            }
36294        },
36295
36296        // private
36297        unregisterCheckable : function(menuItem){
36298            var g = menuItem.group;
36299            if(g){
36300                groups[g].remove(menuItem);
36301                menuItem.un("beforecheckchange", onBeforeCheck);
36302            }
36303        }
36304    };
36305 }();/*
36306  * Based on:
36307  * Ext JS Library 1.1.1
36308  * Copyright(c) 2006-2007, Ext JS, LLC.
36309  *
36310  * Originally Released Under LGPL - original licence link has changed is not relivant.
36311  *
36312  * Fork - LGPL
36313  * <script type="text/javascript">
36314  */
36315  
36316
36317 /**
36318  * @class Roo.menu.BaseItem
36319  * @extends Roo.Component
36320  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36321  * management and base configuration options shared by all menu components.
36322  * @constructor
36323  * Creates a new BaseItem
36324  * @param {Object} config Configuration options
36325  */
36326 Roo.menu.BaseItem = function(config){
36327     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36328
36329     this.addEvents({
36330         /**
36331          * @event click
36332          * Fires when this item is clicked
36333          * @param {Roo.menu.BaseItem} this
36334          * @param {Roo.EventObject} e
36335          */
36336         click: true,
36337         /**
36338          * @event activate
36339          * Fires when this item is activated
36340          * @param {Roo.menu.BaseItem} this
36341          */
36342         activate : true,
36343         /**
36344          * @event deactivate
36345          * Fires when this item is deactivated
36346          * @param {Roo.menu.BaseItem} this
36347          */
36348         deactivate : true
36349     });
36350
36351     if(this.handler){
36352         this.on("click", this.handler, this.scope, true);
36353     }
36354 };
36355
36356 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36357     /**
36358      * @cfg {Function} handler
36359      * A function that will handle the click event of this menu item (defaults to undefined)
36360      */
36361     /**
36362      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36363      */
36364     canActivate : false,
36365     
36366      /**
36367      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36368      */
36369     hidden: false,
36370     
36371     /**
36372      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36373      */
36374     activeClass : "x-menu-item-active",
36375     /**
36376      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36377      */
36378     hideOnClick : true,
36379     /**
36380      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36381      */
36382     hideDelay : 100,
36383
36384     // private
36385     ctype: "Roo.menu.BaseItem",
36386
36387     // private
36388     actionMode : "container",
36389
36390     // private
36391     render : function(container, parentMenu){
36392         this.parentMenu = parentMenu;
36393         Roo.menu.BaseItem.superclass.render.call(this, container);
36394         this.container.menuItemId = this.id;
36395     },
36396
36397     // private
36398     onRender : function(container, position){
36399         this.el = Roo.get(this.el);
36400         container.dom.appendChild(this.el.dom);
36401     },
36402
36403     // private
36404     onClick : function(e){
36405         if(!this.disabled && this.fireEvent("click", this, e) !== false
36406                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36407             this.handleClick(e);
36408         }else{
36409             e.stopEvent();
36410         }
36411     },
36412
36413     // private
36414     activate : function(){
36415         if(this.disabled){
36416             return false;
36417         }
36418         var li = this.container;
36419         li.addClass(this.activeClass);
36420         this.region = li.getRegion().adjust(2, 2, -2, -2);
36421         this.fireEvent("activate", this);
36422         return true;
36423     },
36424
36425     // private
36426     deactivate : function(){
36427         this.container.removeClass(this.activeClass);
36428         this.fireEvent("deactivate", this);
36429     },
36430
36431     // private
36432     shouldDeactivate : function(e){
36433         return !this.region || !this.region.contains(e.getPoint());
36434     },
36435
36436     // private
36437     handleClick : function(e){
36438         if(this.hideOnClick){
36439             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36440         }
36441     },
36442
36443     // private
36444     expandMenu : function(autoActivate){
36445         // do nothing
36446     },
36447
36448     // private
36449     hideMenu : function(){
36450         // do nothing
36451     }
36452 });/*
36453  * Based on:
36454  * Ext JS Library 1.1.1
36455  * Copyright(c) 2006-2007, Ext JS, LLC.
36456  *
36457  * Originally Released Under LGPL - original licence link has changed is not relivant.
36458  *
36459  * Fork - LGPL
36460  * <script type="text/javascript">
36461  */
36462  
36463 /**
36464  * @class Roo.menu.Adapter
36465  * @extends Roo.menu.BaseItem
36466  * 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.
36467  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36468  * @constructor
36469  * Creates a new Adapter
36470  * @param {Object} config Configuration options
36471  */
36472 Roo.menu.Adapter = function(component, config){
36473     Roo.menu.Adapter.superclass.constructor.call(this, config);
36474     this.component = component;
36475 };
36476 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36477     // private
36478     canActivate : true,
36479
36480     // private
36481     onRender : function(container, position){
36482         this.component.render(container);
36483         this.el = this.component.getEl();
36484     },
36485
36486     // private
36487     activate : function(){
36488         if(this.disabled){
36489             return false;
36490         }
36491         this.component.focus();
36492         this.fireEvent("activate", this);
36493         return true;
36494     },
36495
36496     // private
36497     deactivate : function(){
36498         this.fireEvent("deactivate", this);
36499     },
36500
36501     // private
36502     disable : function(){
36503         this.component.disable();
36504         Roo.menu.Adapter.superclass.disable.call(this);
36505     },
36506
36507     // private
36508     enable : function(){
36509         this.component.enable();
36510         Roo.menu.Adapter.superclass.enable.call(this);
36511     }
36512 });/*
36513  * Based on:
36514  * Ext JS Library 1.1.1
36515  * Copyright(c) 2006-2007, Ext JS, LLC.
36516  *
36517  * Originally Released Under LGPL - original licence link has changed is not relivant.
36518  *
36519  * Fork - LGPL
36520  * <script type="text/javascript">
36521  */
36522
36523 /**
36524  * @class Roo.menu.TextItem
36525  * @extends Roo.menu.BaseItem
36526  * Adds a static text string to a menu, usually used as either a heading or group separator.
36527  * Note: old style constructor with text is still supported.
36528  * 
36529  * @constructor
36530  * Creates a new TextItem
36531  * @param {Object} cfg Configuration
36532  */
36533 Roo.menu.TextItem = function(cfg){
36534     if (typeof(cfg) == 'string') {
36535         this.text = cfg;
36536     } else {
36537         Roo.apply(this,cfg);
36538     }
36539     
36540     Roo.menu.TextItem.superclass.constructor.call(this);
36541 };
36542
36543 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36544     /**
36545      * @cfg {Boolean} text Text to show on item.
36546      */
36547     text : '',
36548     
36549     /**
36550      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36551      */
36552     hideOnClick : false,
36553     /**
36554      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36555      */
36556     itemCls : "x-menu-text",
36557
36558     // private
36559     onRender : function(){
36560         var s = document.createElement("span");
36561         s.className = this.itemCls;
36562         s.innerHTML = this.text;
36563         this.el = s;
36564         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36565     }
36566 });/*
36567  * Based on:
36568  * Ext JS Library 1.1.1
36569  * Copyright(c) 2006-2007, Ext JS, LLC.
36570  *
36571  * Originally Released Under LGPL - original licence link has changed is not relivant.
36572  *
36573  * Fork - LGPL
36574  * <script type="text/javascript">
36575  */
36576
36577 /**
36578  * @class Roo.menu.Separator
36579  * @extends Roo.menu.BaseItem
36580  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36581  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36582  * @constructor
36583  * @param {Object} config Configuration options
36584  */
36585 Roo.menu.Separator = function(config){
36586     Roo.menu.Separator.superclass.constructor.call(this, config);
36587 };
36588
36589 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36590     /**
36591      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36592      */
36593     itemCls : "x-menu-sep",
36594     /**
36595      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36596      */
36597     hideOnClick : false,
36598
36599     // private
36600     onRender : function(li){
36601         var s = document.createElement("span");
36602         s.className = this.itemCls;
36603         s.innerHTML = "&#160;";
36604         this.el = s;
36605         li.addClass("x-menu-sep-li");
36606         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36607     }
36608 });/*
36609  * Based on:
36610  * Ext JS Library 1.1.1
36611  * Copyright(c) 2006-2007, Ext JS, LLC.
36612  *
36613  * Originally Released Under LGPL - original licence link has changed is not relivant.
36614  *
36615  * Fork - LGPL
36616  * <script type="text/javascript">
36617  */
36618 /**
36619  * @class Roo.menu.Item
36620  * @extends Roo.menu.BaseItem
36621  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36622  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36623  * activation and click handling.
36624  * @constructor
36625  * Creates a new Item
36626  * @param {Object} config Configuration options
36627  */
36628 Roo.menu.Item = function(config){
36629     Roo.menu.Item.superclass.constructor.call(this, config);
36630     if(this.menu){
36631         this.menu = Roo.menu.MenuMgr.get(this.menu);
36632     }
36633 };
36634 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36635     
36636     /**
36637      * @cfg {String} text
36638      * The text to show on the menu item.
36639      */
36640     text: '',
36641      /**
36642      * @cfg {String} HTML to render in menu
36643      * The text to show on the menu item (HTML version).
36644      */
36645     html: '',
36646     /**
36647      * @cfg {String} icon
36648      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36649      */
36650     icon: undefined,
36651     /**
36652      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36653      */
36654     itemCls : "x-menu-item",
36655     /**
36656      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36657      */
36658     canActivate : true,
36659     /**
36660      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36661      */
36662     showDelay: 200,
36663     // doc'd in BaseItem
36664     hideDelay: 200,
36665
36666     // private
36667     ctype: "Roo.menu.Item",
36668     
36669     // private
36670     onRender : function(container, position){
36671         var el = document.createElement("a");
36672         el.hideFocus = true;
36673         el.unselectable = "on";
36674         el.href = this.href || "#";
36675         if(this.hrefTarget){
36676             el.target = this.hrefTarget;
36677         }
36678         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36679         
36680         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36681         
36682         el.innerHTML = String.format(
36683                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36684                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36685         this.el = el;
36686         Roo.menu.Item.superclass.onRender.call(this, container, position);
36687     },
36688
36689     /**
36690      * Sets the text to display in this menu item
36691      * @param {String} text The text to display
36692      * @param {Boolean} isHTML true to indicate text is pure html.
36693      */
36694     setText : function(text, isHTML){
36695         if (isHTML) {
36696             this.html = text;
36697         } else {
36698             this.text = text;
36699             this.html = '';
36700         }
36701         if(this.rendered){
36702             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36703      
36704             this.el.update(String.format(
36705                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36706                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36707             this.parentMenu.autoWidth();
36708         }
36709     },
36710
36711     // private
36712     handleClick : function(e){
36713         if(!this.href){ // if no link defined, stop the event automatically
36714             e.stopEvent();
36715         }
36716         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36717     },
36718
36719     // private
36720     activate : function(autoExpand){
36721         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36722             this.focus();
36723             if(autoExpand){
36724                 this.expandMenu();
36725             }
36726         }
36727         return true;
36728     },
36729
36730     // private
36731     shouldDeactivate : function(e){
36732         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36733             if(this.menu && this.menu.isVisible()){
36734                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36735             }
36736             return true;
36737         }
36738         return false;
36739     },
36740
36741     // private
36742     deactivate : function(){
36743         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36744         this.hideMenu();
36745     },
36746
36747     // private
36748     expandMenu : function(autoActivate){
36749         if(!this.disabled && this.menu){
36750             clearTimeout(this.hideTimer);
36751             delete this.hideTimer;
36752             if(!this.menu.isVisible() && !this.showTimer){
36753                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36754             }else if (this.menu.isVisible() && autoActivate){
36755                 this.menu.tryActivate(0, 1);
36756             }
36757         }
36758     },
36759
36760     // private
36761     deferExpand : function(autoActivate){
36762         delete this.showTimer;
36763         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36764         if(autoActivate){
36765             this.menu.tryActivate(0, 1);
36766         }
36767     },
36768
36769     // private
36770     hideMenu : function(){
36771         clearTimeout(this.showTimer);
36772         delete this.showTimer;
36773         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36774             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36775         }
36776     },
36777
36778     // private
36779     deferHide : function(){
36780         delete this.hideTimer;
36781         this.menu.hide();
36782     }
36783 });/*
36784  * Based on:
36785  * Ext JS Library 1.1.1
36786  * Copyright(c) 2006-2007, Ext JS, LLC.
36787  *
36788  * Originally Released Under LGPL - original licence link has changed is not relivant.
36789  *
36790  * Fork - LGPL
36791  * <script type="text/javascript">
36792  */
36793  
36794 /**
36795  * @class Roo.menu.CheckItem
36796  * @extends Roo.menu.Item
36797  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36798  * @constructor
36799  * Creates a new CheckItem
36800  * @param {Object} config Configuration options
36801  */
36802 Roo.menu.CheckItem = function(config){
36803     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36804     this.addEvents({
36805         /**
36806          * @event beforecheckchange
36807          * Fires before the checked value is set, providing an opportunity to cancel if needed
36808          * @param {Roo.menu.CheckItem} this
36809          * @param {Boolean} checked The new checked value that will be set
36810          */
36811         "beforecheckchange" : true,
36812         /**
36813          * @event checkchange
36814          * Fires after the checked value has been set
36815          * @param {Roo.menu.CheckItem} this
36816          * @param {Boolean} checked The checked value that was set
36817          */
36818         "checkchange" : true
36819     });
36820     if(this.checkHandler){
36821         this.on('checkchange', this.checkHandler, this.scope);
36822     }
36823 };
36824 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36825     /**
36826      * @cfg {String} group
36827      * All check items with the same group name will automatically be grouped into a single-select
36828      * radio button group (defaults to '')
36829      */
36830     /**
36831      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36832      */
36833     itemCls : "x-menu-item x-menu-check-item",
36834     /**
36835      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36836      */
36837     groupClass : "x-menu-group-item",
36838
36839     /**
36840      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36841      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36842      * initialized with checked = true will be rendered as checked.
36843      */
36844     checked: false,
36845
36846     // private
36847     ctype: "Roo.menu.CheckItem",
36848
36849     // private
36850     onRender : function(c){
36851         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36852         if(this.group){
36853             this.el.addClass(this.groupClass);
36854         }
36855         Roo.menu.MenuMgr.registerCheckable(this);
36856         if(this.checked){
36857             this.checked = false;
36858             this.setChecked(true, true);
36859         }
36860     },
36861
36862     // private
36863     destroy : function(){
36864         if(this.rendered){
36865             Roo.menu.MenuMgr.unregisterCheckable(this);
36866         }
36867         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36868     },
36869
36870     /**
36871      * Set the checked state of this item
36872      * @param {Boolean} checked The new checked value
36873      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36874      */
36875     setChecked : function(state, suppressEvent){
36876         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36877             if(this.container){
36878                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36879             }
36880             this.checked = state;
36881             if(suppressEvent !== true){
36882                 this.fireEvent("checkchange", this, state);
36883             }
36884         }
36885     },
36886
36887     // private
36888     handleClick : function(e){
36889        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36890            this.setChecked(!this.checked);
36891        }
36892        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36893     }
36894 });/*
36895  * Based on:
36896  * Ext JS Library 1.1.1
36897  * Copyright(c) 2006-2007, Ext JS, LLC.
36898  *
36899  * Originally Released Under LGPL - original licence link has changed is not relivant.
36900  *
36901  * Fork - LGPL
36902  * <script type="text/javascript">
36903  */
36904  
36905 /**
36906  * @class Roo.menu.DateItem
36907  * @extends Roo.menu.Adapter
36908  * A menu item that wraps the {@link Roo.DatPicker} component.
36909  * @constructor
36910  * Creates a new DateItem
36911  * @param {Object} config Configuration options
36912  */
36913 Roo.menu.DateItem = function(config){
36914     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36915     /** The Roo.DatePicker object @type Roo.DatePicker */
36916     this.picker = this.component;
36917     this.addEvents({select: true});
36918     
36919     this.picker.on("render", function(picker){
36920         picker.getEl().swallowEvent("click");
36921         picker.container.addClass("x-menu-date-item");
36922     });
36923
36924     this.picker.on("select", this.onSelect, this);
36925 };
36926
36927 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36928     // private
36929     onSelect : function(picker, date){
36930         this.fireEvent("select", this, date, picker);
36931         Roo.menu.DateItem.superclass.handleClick.call(this);
36932     }
36933 });/*
36934  * Based on:
36935  * Ext JS Library 1.1.1
36936  * Copyright(c) 2006-2007, Ext JS, LLC.
36937  *
36938  * Originally Released Under LGPL - original licence link has changed is not relivant.
36939  *
36940  * Fork - LGPL
36941  * <script type="text/javascript">
36942  */
36943  
36944 /**
36945  * @class Roo.menu.ColorItem
36946  * @extends Roo.menu.Adapter
36947  * A menu item that wraps the {@link Roo.ColorPalette} component.
36948  * @constructor
36949  * Creates a new ColorItem
36950  * @param {Object} config Configuration options
36951  */
36952 Roo.menu.ColorItem = function(config){
36953     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36954     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36955     this.palette = this.component;
36956     this.relayEvents(this.palette, ["select"]);
36957     if(this.selectHandler){
36958         this.on('select', this.selectHandler, this.scope);
36959     }
36960 };
36961 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36962  * Based on:
36963  * Ext JS Library 1.1.1
36964  * Copyright(c) 2006-2007, Ext JS, LLC.
36965  *
36966  * Originally Released Under LGPL - original licence link has changed is not relivant.
36967  *
36968  * Fork - LGPL
36969  * <script type="text/javascript">
36970  */
36971  
36972
36973 /**
36974  * @class Roo.menu.DateMenu
36975  * @extends Roo.menu.Menu
36976  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36977  * @constructor
36978  * Creates a new DateMenu
36979  * @param {Object} config Configuration options
36980  */
36981 Roo.menu.DateMenu = function(config){
36982     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36983     this.plain = true;
36984     var di = new Roo.menu.DateItem(config);
36985     this.add(di);
36986     /**
36987      * The {@link Roo.DatePicker} instance for this DateMenu
36988      * @type DatePicker
36989      */
36990     this.picker = di.picker;
36991     /**
36992      * @event select
36993      * @param {DatePicker} picker
36994      * @param {Date} date
36995      */
36996     this.relayEvents(di, ["select"]);
36997     this.on('beforeshow', function(){
36998         if(this.picker){
36999             this.picker.hideMonthPicker(false);
37000         }
37001     }, this);
37002 };
37003 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
37004     cls:'x-date-menu'
37005 });/*
37006  * Based on:
37007  * Ext JS Library 1.1.1
37008  * Copyright(c) 2006-2007, Ext JS, LLC.
37009  *
37010  * Originally Released Under LGPL - original licence link has changed is not relivant.
37011  *
37012  * Fork - LGPL
37013  * <script type="text/javascript">
37014  */
37015  
37016
37017 /**
37018  * @class Roo.menu.ColorMenu
37019  * @extends Roo.menu.Menu
37020  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37021  * @constructor
37022  * Creates a new ColorMenu
37023  * @param {Object} config Configuration options
37024  */
37025 Roo.menu.ColorMenu = function(config){
37026     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37027     this.plain = true;
37028     var ci = new Roo.menu.ColorItem(config);
37029     this.add(ci);
37030     /**
37031      * The {@link Roo.ColorPalette} instance for this ColorMenu
37032      * @type ColorPalette
37033      */
37034     this.palette = ci.palette;
37035     /**
37036      * @event select
37037      * @param {ColorPalette} palette
37038      * @param {String} color
37039      */
37040     this.relayEvents(ci, ["select"]);
37041 };
37042 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37043  * Based on:
37044  * Ext JS Library 1.1.1
37045  * Copyright(c) 2006-2007, Ext JS, LLC.
37046  *
37047  * Originally Released Under LGPL - original licence link has changed is not relivant.
37048  *
37049  * Fork - LGPL
37050  * <script type="text/javascript">
37051  */
37052  
37053 /**
37054  * @class Roo.form.Field
37055  * @extends Roo.BoxComponent
37056  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37057  * @constructor
37058  * Creates a new Field
37059  * @param {Object} config Configuration options
37060  */
37061 Roo.form.Field = function(config){
37062     Roo.form.Field.superclass.constructor.call(this, config);
37063 };
37064
37065 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37066     /**
37067      * @cfg {String} fieldLabel Label to use when rendering a form.
37068      */
37069        /**
37070      * @cfg {String} qtip Mouse over tip
37071      */
37072      
37073     /**
37074      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37075      */
37076     invalidClass : "x-form-invalid",
37077     /**
37078      * @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")
37079      */
37080     invalidText : "The value in this field is invalid",
37081     /**
37082      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37083      */
37084     focusClass : "x-form-focus",
37085     /**
37086      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37087       automatic validation (defaults to "keyup").
37088      */
37089     validationEvent : "keyup",
37090     /**
37091      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37092      */
37093     validateOnBlur : true,
37094     /**
37095      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37096      */
37097     validationDelay : 250,
37098     /**
37099      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37100      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37101      */
37102     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37103     /**
37104      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37105      */
37106     fieldClass : "x-form-field",
37107     /**
37108      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37109      *<pre>
37110 Value         Description
37111 -----------   ----------------------------------------------------------------------
37112 qtip          Display a quick tip when the user hovers over the field
37113 title         Display a default browser title attribute popup
37114 under         Add a block div beneath the field containing the error text
37115 side          Add an error icon to the right of the field with a popup on hover
37116 [element id]  Add the error text directly to the innerHTML of the specified element
37117 </pre>
37118      */
37119     msgTarget : 'qtip',
37120     /**
37121      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37122      */
37123     msgFx : 'normal',
37124
37125     /**
37126      * @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.
37127      */
37128     readOnly : false,
37129
37130     /**
37131      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37132      */
37133     disabled : false,
37134
37135     /**
37136      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37137      */
37138     inputType : undefined,
37139     
37140     /**
37141      * @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).
37142          */
37143         tabIndex : undefined,
37144         
37145     // private
37146     isFormField : true,
37147
37148     // private
37149     hasFocus : false,
37150     /**
37151      * @property {Roo.Element} fieldEl
37152      * Element Containing the rendered Field (with label etc.)
37153      */
37154     /**
37155      * @cfg {Mixed} value A value to initialize this field with.
37156      */
37157     value : undefined,
37158
37159     /**
37160      * @cfg {String} name The field's HTML name attribute.
37161      */
37162     /**
37163      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37164      */
37165
37166         // private ??
37167         initComponent : function(){
37168         Roo.form.Field.superclass.initComponent.call(this);
37169         this.addEvents({
37170             /**
37171              * @event focus
37172              * Fires when this field receives input focus.
37173              * @param {Roo.form.Field} this
37174              */
37175             focus : true,
37176             /**
37177              * @event blur
37178              * Fires when this field loses input focus.
37179              * @param {Roo.form.Field} this
37180              */
37181             blur : true,
37182             /**
37183              * @event specialkey
37184              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37185              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37186              * @param {Roo.form.Field} this
37187              * @param {Roo.EventObject} e The event object
37188              */
37189             specialkey : true,
37190             /**
37191              * @event change
37192              * Fires just before the field blurs if the field value has changed.
37193              * @param {Roo.form.Field} this
37194              * @param {Mixed} newValue The new value
37195              * @param {Mixed} oldValue The original value
37196              */
37197             change : true,
37198             /**
37199              * @event invalid
37200              * Fires after the field has been marked as invalid.
37201              * @param {Roo.form.Field} this
37202              * @param {String} msg The validation message
37203              */
37204             invalid : true,
37205             /**
37206              * @event valid
37207              * Fires after the field has been validated with no errors.
37208              * @param {Roo.form.Field} this
37209              */
37210             valid : true,
37211              /**
37212              * @event keyup
37213              * Fires after the key up
37214              * @param {Roo.form.Field} this
37215              * @param {Roo.EventObject}  e The event Object
37216              */
37217             keyup : true
37218         });
37219     },
37220
37221     /**
37222      * Returns the name attribute of the field if available
37223      * @return {String} name The field name
37224      */
37225     getName: function(){
37226          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37227     },
37228
37229     // private
37230     onRender : function(ct, position){
37231         Roo.form.Field.superclass.onRender.call(this, ct, position);
37232         if(!this.el){
37233             var cfg = this.getAutoCreate();
37234             if(!cfg.name){
37235                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37236             }
37237             if (!cfg.name.length) {
37238                 delete cfg.name;
37239             }
37240             if(this.inputType){
37241                 cfg.type = this.inputType;
37242             }
37243             this.el = ct.createChild(cfg, position);
37244         }
37245         var type = this.el.dom.type;
37246         if(type){
37247             if(type == 'password'){
37248                 type = 'text';
37249             }
37250             this.el.addClass('x-form-'+type);
37251         }
37252         if(this.readOnly){
37253             this.el.dom.readOnly = true;
37254         }
37255         if(this.tabIndex !== undefined){
37256             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37257         }
37258
37259         this.el.addClass([this.fieldClass, this.cls]);
37260         this.initValue();
37261     },
37262
37263     /**
37264      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37265      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37266      * @return {Roo.form.Field} this
37267      */
37268     applyTo : function(target){
37269         this.allowDomMove = false;
37270         this.el = Roo.get(target);
37271         this.render(this.el.dom.parentNode);
37272         return this;
37273     },
37274
37275     // private
37276     initValue : function(){
37277         if(this.value !== undefined){
37278             this.setValue(this.value);
37279         }else if(this.el.dom.value.length > 0){
37280             this.setValue(this.el.dom.value);
37281         }
37282     },
37283
37284     /**
37285      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37286      */
37287     isDirty : function() {
37288         if(this.disabled) {
37289             return false;
37290         }
37291         return String(this.getValue()) !== String(this.originalValue);
37292     },
37293
37294     // private
37295     afterRender : function(){
37296         Roo.form.Field.superclass.afterRender.call(this);
37297         this.initEvents();
37298     },
37299
37300     // private
37301     fireKey : function(e){
37302         //Roo.log('field ' + e.getKey());
37303         if(e.isNavKeyPress()){
37304             this.fireEvent("specialkey", this, e);
37305         }
37306     },
37307
37308     /**
37309      * Resets the current field value to the originally loaded value and clears any validation messages
37310      */
37311     reset : function(){
37312         this.setValue(this.resetValue);
37313         this.clearInvalid();
37314     },
37315
37316     // private
37317     initEvents : function(){
37318         // safari killled keypress - so keydown is now used..
37319         this.el.on("keydown" , this.fireKey,  this);
37320         this.el.on("focus", this.onFocus,  this);
37321         this.el.on("blur", this.onBlur,  this);
37322         this.el.relayEvent('keyup', this);
37323
37324         // reference to original value for reset
37325         this.originalValue = this.getValue();
37326         this.resetValue =  this.getValue();
37327     },
37328
37329     // private
37330     onFocus : function(){
37331         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37332             this.el.addClass(this.focusClass);
37333         }
37334         if(!this.hasFocus){
37335             this.hasFocus = true;
37336             this.startValue = this.getValue();
37337             this.fireEvent("focus", this);
37338         }
37339     },
37340
37341     beforeBlur : Roo.emptyFn,
37342
37343     // private
37344     onBlur : function(){
37345         this.beforeBlur();
37346         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37347             this.el.removeClass(this.focusClass);
37348         }
37349         this.hasFocus = false;
37350         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37351             this.validate();
37352         }
37353         var v = this.getValue();
37354         if(String(v) !== String(this.startValue)){
37355             this.fireEvent('change', this, v, this.startValue);
37356         }
37357         this.fireEvent("blur", this);
37358     },
37359
37360     /**
37361      * Returns whether or not the field value is currently valid
37362      * @param {Boolean} preventMark True to disable marking the field invalid
37363      * @return {Boolean} True if the value is valid, else false
37364      */
37365     isValid : function(preventMark){
37366         if(this.disabled){
37367             return true;
37368         }
37369         var restore = this.preventMark;
37370         this.preventMark = preventMark === true;
37371         var v = this.validateValue(this.processValue(this.getRawValue()));
37372         this.preventMark = restore;
37373         return v;
37374     },
37375
37376     /**
37377      * Validates the field value
37378      * @return {Boolean} True if the value is valid, else false
37379      */
37380     validate : function(){
37381         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37382             this.clearInvalid();
37383             return true;
37384         }
37385         return false;
37386     },
37387
37388     processValue : function(value){
37389         return value;
37390     },
37391
37392     // private
37393     // Subclasses should provide the validation implementation by overriding this
37394     validateValue : function(value){
37395         return true;
37396     },
37397
37398     /**
37399      * Mark this field as invalid
37400      * @param {String} msg The validation message
37401      */
37402     markInvalid : function(msg){
37403         if(!this.rendered || this.preventMark){ // not rendered
37404             return;
37405         }
37406         
37407         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37408         
37409         obj.el.addClass(this.invalidClass);
37410         msg = msg || this.invalidText;
37411         switch(this.msgTarget){
37412             case 'qtip':
37413                 obj.el.dom.qtip = msg;
37414                 obj.el.dom.qclass = 'x-form-invalid-tip';
37415                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37416                     Roo.QuickTips.enable();
37417                 }
37418                 break;
37419             case 'title':
37420                 this.el.dom.title = msg;
37421                 break;
37422             case 'under':
37423                 if(!this.errorEl){
37424                     var elp = this.el.findParent('.x-form-element', 5, true);
37425                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37426                     this.errorEl.setWidth(elp.getWidth(true)-20);
37427                 }
37428                 this.errorEl.update(msg);
37429                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37430                 break;
37431             case 'side':
37432                 if(!this.errorIcon){
37433                     var elp = this.el.findParent('.x-form-element', 5, true);
37434                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37435                 }
37436                 this.alignErrorIcon();
37437                 this.errorIcon.dom.qtip = msg;
37438                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37439                 this.errorIcon.show();
37440                 this.on('resize', this.alignErrorIcon, this);
37441                 break;
37442             default:
37443                 var t = Roo.getDom(this.msgTarget);
37444                 t.innerHTML = msg;
37445                 t.style.display = this.msgDisplay;
37446                 break;
37447         }
37448         this.fireEvent('invalid', this, msg);
37449     },
37450
37451     // private
37452     alignErrorIcon : function(){
37453         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37454     },
37455
37456     /**
37457      * Clear any invalid styles/messages for this field
37458      */
37459     clearInvalid : function(){
37460         if(!this.rendered || this.preventMark){ // not rendered
37461             return;
37462         }
37463         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37464         
37465         obj.el.removeClass(this.invalidClass);
37466         switch(this.msgTarget){
37467             case 'qtip':
37468                 obj.el.dom.qtip = '';
37469                 break;
37470             case 'title':
37471                 this.el.dom.title = '';
37472                 break;
37473             case 'under':
37474                 if(this.errorEl){
37475                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37476                 }
37477                 break;
37478             case 'side':
37479                 if(this.errorIcon){
37480                     this.errorIcon.dom.qtip = '';
37481                     this.errorIcon.hide();
37482                     this.un('resize', this.alignErrorIcon, this);
37483                 }
37484                 break;
37485             default:
37486                 var t = Roo.getDom(this.msgTarget);
37487                 t.innerHTML = '';
37488                 t.style.display = 'none';
37489                 break;
37490         }
37491         this.fireEvent('valid', this);
37492     },
37493
37494     /**
37495      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37496      * @return {Mixed} value The field value
37497      */
37498     getRawValue : function(){
37499         var v = this.el.getValue();
37500         
37501         return v;
37502     },
37503
37504     /**
37505      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37506      * @return {Mixed} value The field value
37507      */
37508     getValue : function(){
37509         var v = this.el.getValue();
37510          
37511         return v;
37512     },
37513
37514     /**
37515      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37516      * @param {Mixed} value The value to set
37517      */
37518     setRawValue : function(v){
37519         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37520     },
37521
37522     /**
37523      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37524      * @param {Mixed} value The value to set
37525      */
37526     setValue : function(v){
37527         this.value = v;
37528         if(this.rendered){
37529             this.el.dom.value = (v === null || v === undefined ? '' : v);
37530              this.validate();
37531         }
37532     },
37533
37534     adjustSize : function(w, h){
37535         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37536         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37537         return s;
37538     },
37539
37540     adjustWidth : function(tag, w){
37541         tag = tag.toLowerCase();
37542         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37543             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37544                 if(tag == 'input'){
37545                     return w + 2;
37546                 }
37547                 if(tag == 'textarea'){
37548                     return w-2;
37549                 }
37550             }else if(Roo.isOpera){
37551                 if(tag == 'input'){
37552                     return w + 2;
37553                 }
37554                 if(tag == 'textarea'){
37555                     return w-2;
37556                 }
37557             }
37558         }
37559         return w;
37560     }
37561 });
37562
37563
37564 // anything other than normal should be considered experimental
37565 Roo.form.Field.msgFx = {
37566     normal : {
37567         show: function(msgEl, f){
37568             msgEl.setDisplayed('block');
37569         },
37570
37571         hide : function(msgEl, f){
37572             msgEl.setDisplayed(false).update('');
37573         }
37574     },
37575
37576     slide : {
37577         show: function(msgEl, f){
37578             msgEl.slideIn('t', {stopFx:true});
37579         },
37580
37581         hide : function(msgEl, f){
37582             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37583         }
37584     },
37585
37586     slideRight : {
37587         show: function(msgEl, f){
37588             msgEl.fixDisplay();
37589             msgEl.alignTo(f.el, 'tl-tr');
37590             msgEl.slideIn('l', {stopFx:true});
37591         },
37592
37593         hide : function(msgEl, f){
37594             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37595         }
37596     }
37597 };/*
37598  * Based on:
37599  * Ext JS Library 1.1.1
37600  * Copyright(c) 2006-2007, Ext JS, LLC.
37601  *
37602  * Originally Released Under LGPL - original licence link has changed is not relivant.
37603  *
37604  * Fork - LGPL
37605  * <script type="text/javascript">
37606  */
37607  
37608
37609 /**
37610  * @class Roo.form.TextField
37611  * @extends Roo.form.Field
37612  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37613  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37614  * @constructor
37615  * Creates a new TextField
37616  * @param {Object} config Configuration options
37617  */
37618 Roo.form.TextField = function(config){
37619     Roo.form.TextField.superclass.constructor.call(this, config);
37620     this.addEvents({
37621         /**
37622          * @event autosize
37623          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37624          * according to the default logic, but this event provides a hook for the developer to apply additional
37625          * logic at runtime to resize the field if needed.
37626              * @param {Roo.form.Field} this This text field
37627              * @param {Number} width The new field width
37628              */
37629         autosize : true
37630     });
37631 };
37632
37633 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37634     /**
37635      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37636      */
37637     grow : false,
37638     /**
37639      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37640      */
37641     growMin : 30,
37642     /**
37643      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37644      */
37645     growMax : 800,
37646     /**
37647      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37648      */
37649     vtype : null,
37650     /**
37651      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37652      */
37653     maskRe : null,
37654     /**
37655      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37656      */
37657     disableKeyFilter : false,
37658     /**
37659      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37660      */
37661     allowBlank : true,
37662     /**
37663      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37664      */
37665     minLength : 0,
37666     /**
37667      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37668      */
37669     maxLength : Number.MAX_VALUE,
37670     /**
37671      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37672      */
37673     minLengthText : "The minimum length for this field is {0}",
37674     /**
37675      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37676      */
37677     maxLengthText : "The maximum length for this field is {0}",
37678     /**
37679      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37680      */
37681     selectOnFocus : false,
37682     /**
37683      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37684      */
37685     blankText : "This field is required",
37686     /**
37687      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37688      * If available, this function will be called only after the basic validators all return true, and will be passed the
37689      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37690      */
37691     validator : null,
37692     /**
37693      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37694      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37695      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37696      */
37697     regex : null,
37698     /**
37699      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37700      */
37701     regexText : "",
37702     /**
37703      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37704      */
37705     emptyText : null,
37706    
37707
37708     // private
37709     initEvents : function()
37710     {
37711         if (this.emptyText) {
37712             this.el.attr('placeholder', this.emptyText);
37713         }
37714         
37715         Roo.form.TextField.superclass.initEvents.call(this);
37716         if(this.validationEvent == 'keyup'){
37717             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37718             this.el.on('keyup', this.filterValidation, this);
37719         }
37720         else if(this.validationEvent !== false){
37721             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37722         }
37723         
37724         if(this.selectOnFocus){
37725             this.on("focus", this.preFocus, this);
37726             
37727         }
37728         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37729             this.el.on("keypress", this.filterKeys, this);
37730         }
37731         if(this.grow){
37732             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37733             this.el.on("click", this.autoSize,  this);
37734         }
37735         if(this.el.is('input[type=password]') && Roo.isSafari){
37736             this.el.on('keydown', this.SafariOnKeyDown, this);
37737         }
37738     },
37739
37740     processValue : function(value){
37741         if(this.stripCharsRe){
37742             var newValue = value.replace(this.stripCharsRe, '');
37743             if(newValue !== value){
37744                 this.setRawValue(newValue);
37745                 return newValue;
37746             }
37747         }
37748         return value;
37749     },
37750
37751     filterValidation : function(e){
37752         if(!e.isNavKeyPress()){
37753             this.validationTask.delay(this.validationDelay);
37754         }
37755     },
37756
37757     // private
37758     onKeyUp : function(e){
37759         if(!e.isNavKeyPress()){
37760             this.autoSize();
37761         }
37762     },
37763
37764     /**
37765      * Resets the current field value to the originally-loaded value and clears any validation messages.
37766      *  
37767      */
37768     reset : function(){
37769         Roo.form.TextField.superclass.reset.call(this);
37770        
37771     },
37772
37773     
37774     // private
37775     preFocus : function(){
37776         
37777         if(this.selectOnFocus){
37778             this.el.dom.select();
37779         }
37780     },
37781
37782     
37783     // private
37784     filterKeys : function(e){
37785         var k = e.getKey();
37786         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37787             return;
37788         }
37789         var c = e.getCharCode(), cc = String.fromCharCode(c);
37790         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37791             return;
37792         }
37793         if(!this.maskRe.test(cc)){
37794             e.stopEvent();
37795         }
37796     },
37797
37798     setValue : function(v){
37799         
37800         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37801         
37802         this.autoSize();
37803     },
37804
37805     /**
37806      * Validates a value according to the field's validation rules and marks the field as invalid
37807      * if the validation fails
37808      * @param {Mixed} value The value to validate
37809      * @return {Boolean} True if the value is valid, else false
37810      */
37811     validateValue : function(value){
37812         if(value.length < 1)  { // if it's blank
37813              if(this.allowBlank){
37814                 this.clearInvalid();
37815                 return true;
37816              }else{
37817                 this.markInvalid(this.blankText);
37818                 return false;
37819              }
37820         }
37821         if(value.length < this.minLength){
37822             this.markInvalid(String.format(this.minLengthText, this.minLength));
37823             return false;
37824         }
37825         if(value.length > this.maxLength){
37826             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37827             return false;
37828         }
37829         if(this.vtype){
37830             var vt = Roo.form.VTypes;
37831             if(!vt[this.vtype](value, this)){
37832                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37833                 return false;
37834             }
37835         }
37836         if(typeof this.validator == "function"){
37837             var msg = this.validator(value);
37838             if(msg !== true){
37839                 this.markInvalid(msg);
37840                 return false;
37841             }
37842         }
37843         if(this.regex && !this.regex.test(value)){
37844             this.markInvalid(this.regexText);
37845             return false;
37846         }
37847         return true;
37848     },
37849
37850     /**
37851      * Selects text in this field
37852      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37853      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37854      */
37855     selectText : function(start, end){
37856         var v = this.getRawValue();
37857         if(v.length > 0){
37858             start = start === undefined ? 0 : start;
37859             end = end === undefined ? v.length : end;
37860             var d = this.el.dom;
37861             if(d.setSelectionRange){
37862                 d.setSelectionRange(start, end);
37863             }else if(d.createTextRange){
37864                 var range = d.createTextRange();
37865                 range.moveStart("character", start);
37866                 range.moveEnd("character", v.length-end);
37867                 range.select();
37868             }
37869         }
37870     },
37871
37872     /**
37873      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37874      * This only takes effect if grow = true, and fires the autosize event.
37875      */
37876     autoSize : function(){
37877         if(!this.grow || !this.rendered){
37878             return;
37879         }
37880         if(!this.metrics){
37881             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37882         }
37883         var el = this.el;
37884         var v = el.dom.value;
37885         var d = document.createElement('div');
37886         d.appendChild(document.createTextNode(v));
37887         v = d.innerHTML;
37888         d = null;
37889         v += "&#160;";
37890         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37891         this.el.setWidth(w);
37892         this.fireEvent("autosize", this, w);
37893     },
37894     
37895     // private
37896     SafariOnKeyDown : function(event)
37897     {
37898         // this is a workaround for a password hang bug on chrome/ webkit.
37899         
37900         var isSelectAll = false;
37901         
37902         if(this.el.dom.selectionEnd > 0){
37903             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37904         }
37905         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37906             event.preventDefault();
37907             this.setValue('');
37908             return;
37909         }
37910         
37911         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37912             
37913             event.preventDefault();
37914             // this is very hacky as keydown always get's upper case.
37915             
37916             var cc = String.fromCharCode(event.getCharCode());
37917             
37918             
37919             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37920             
37921         }
37922         
37923         
37924     }
37925 });/*
37926  * Based on:
37927  * Ext JS Library 1.1.1
37928  * Copyright(c) 2006-2007, Ext JS, LLC.
37929  *
37930  * Originally Released Under LGPL - original licence link has changed is not relivant.
37931  *
37932  * Fork - LGPL
37933  * <script type="text/javascript">
37934  */
37935  
37936 /**
37937  * @class Roo.form.Hidden
37938  * @extends Roo.form.TextField
37939  * Simple Hidden element used on forms 
37940  * 
37941  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37942  * 
37943  * @constructor
37944  * Creates a new Hidden form element.
37945  * @param {Object} config Configuration options
37946  */
37947
37948
37949
37950 // easy hidden field...
37951 Roo.form.Hidden = function(config){
37952     Roo.form.Hidden.superclass.constructor.call(this, config);
37953 };
37954   
37955 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37956     fieldLabel:      '',
37957     inputType:      'hidden',
37958     width:          50,
37959     allowBlank:     true,
37960     labelSeparator: '',
37961     hidden:         true,
37962     itemCls :       'x-form-item-display-none'
37963
37964
37965 });
37966
37967
37968 /*
37969  * Based on:
37970  * Ext JS Library 1.1.1
37971  * Copyright(c) 2006-2007, Ext JS, LLC.
37972  *
37973  * Originally Released Under LGPL - original licence link has changed is not relivant.
37974  *
37975  * Fork - LGPL
37976  * <script type="text/javascript">
37977  */
37978  
37979 /**
37980  * @class Roo.form.TriggerField
37981  * @extends Roo.form.TextField
37982  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37983  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37984  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37985  * for which you can provide a custom implementation.  For example:
37986  * <pre><code>
37987 var trigger = new Roo.form.TriggerField();
37988 trigger.onTriggerClick = myTriggerFn;
37989 trigger.applyTo('my-field');
37990 </code></pre>
37991  *
37992  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37993  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37994  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37995  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37996  * @constructor
37997  * Create a new TriggerField.
37998  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37999  * to the base TextField)
38000  */
38001 Roo.form.TriggerField = function(config){
38002     this.mimicing = false;
38003     Roo.form.TriggerField.superclass.constructor.call(this, config);
38004 };
38005
38006 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
38007     /**
38008      * @cfg {String} triggerClass A CSS class to apply to the trigger
38009      */
38010     /**
38011      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38012      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
38013      */
38014     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38015     /**
38016      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38017      */
38018     hideTrigger:false,
38019
38020     /** @cfg {Boolean} grow @hide */
38021     /** @cfg {Number} growMin @hide */
38022     /** @cfg {Number} growMax @hide */
38023
38024     /**
38025      * @hide 
38026      * @method
38027      */
38028     autoSize: Roo.emptyFn,
38029     // private
38030     monitorTab : true,
38031     // private
38032     deferHeight : true,
38033
38034     
38035     actionMode : 'wrap',
38036     // private
38037     onResize : function(w, h){
38038         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38039         if(typeof w == 'number'){
38040             var x = w - this.trigger.getWidth();
38041             this.el.setWidth(this.adjustWidth('input', x));
38042             this.trigger.setStyle('left', x+'px');
38043         }
38044     },
38045
38046     // private
38047     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38048
38049     // private
38050     getResizeEl : function(){
38051         return this.wrap;
38052     },
38053
38054     // private
38055     getPositionEl : function(){
38056         return this.wrap;
38057     },
38058
38059     // private
38060     alignErrorIcon : function(){
38061         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38062     },
38063
38064     // private
38065     onRender : function(ct, position){
38066         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38067         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38068         this.trigger = this.wrap.createChild(this.triggerConfig ||
38069                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38070         if(this.hideTrigger){
38071             this.trigger.setDisplayed(false);
38072         }
38073         this.initTrigger();
38074         if(!this.width){
38075             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38076         }
38077     },
38078
38079     // private
38080     initTrigger : function(){
38081         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38082         this.trigger.addClassOnOver('x-form-trigger-over');
38083         this.trigger.addClassOnClick('x-form-trigger-click');
38084     },
38085
38086     // private
38087     onDestroy : function(){
38088         if(this.trigger){
38089             this.trigger.removeAllListeners();
38090             this.trigger.remove();
38091         }
38092         if(this.wrap){
38093             this.wrap.remove();
38094         }
38095         Roo.form.TriggerField.superclass.onDestroy.call(this);
38096     },
38097
38098     // private
38099     onFocus : function(){
38100         Roo.form.TriggerField.superclass.onFocus.call(this);
38101         if(!this.mimicing){
38102             this.wrap.addClass('x-trigger-wrap-focus');
38103             this.mimicing = true;
38104             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38105             if(this.monitorTab){
38106                 this.el.on("keydown", this.checkTab, this);
38107             }
38108         }
38109     },
38110
38111     // private
38112     checkTab : function(e){
38113         if(e.getKey() == e.TAB){
38114             this.triggerBlur();
38115         }
38116     },
38117
38118     // private
38119     onBlur : function(){
38120         // do nothing
38121     },
38122
38123     // private
38124     mimicBlur : function(e, t){
38125         if(!this.wrap.contains(t) && this.validateBlur()){
38126             this.triggerBlur();
38127         }
38128     },
38129
38130     // private
38131     triggerBlur : function(){
38132         this.mimicing = false;
38133         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38134         if(this.monitorTab){
38135             this.el.un("keydown", this.checkTab, this);
38136         }
38137         this.wrap.removeClass('x-trigger-wrap-focus');
38138         Roo.form.TriggerField.superclass.onBlur.call(this);
38139     },
38140
38141     // private
38142     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38143     validateBlur : function(e, t){
38144         return true;
38145     },
38146
38147     // private
38148     onDisable : function(){
38149         Roo.form.TriggerField.superclass.onDisable.call(this);
38150         if(this.wrap){
38151             this.wrap.addClass('x-item-disabled');
38152         }
38153     },
38154
38155     // private
38156     onEnable : function(){
38157         Roo.form.TriggerField.superclass.onEnable.call(this);
38158         if(this.wrap){
38159             this.wrap.removeClass('x-item-disabled');
38160         }
38161     },
38162
38163     // private
38164     onShow : function(){
38165         var ae = this.getActionEl();
38166         
38167         if(ae){
38168             ae.dom.style.display = '';
38169             ae.dom.style.visibility = 'visible';
38170         }
38171     },
38172
38173     // private
38174     
38175     onHide : function(){
38176         var ae = this.getActionEl();
38177         ae.dom.style.display = 'none';
38178     },
38179
38180     /**
38181      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38182      * by an implementing function.
38183      * @method
38184      * @param {EventObject} e
38185      */
38186     onTriggerClick : Roo.emptyFn
38187 });
38188
38189 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38190 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38191 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38192 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38193     initComponent : function(){
38194         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38195
38196         this.triggerConfig = {
38197             tag:'span', cls:'x-form-twin-triggers', cn:[
38198             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38199             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38200         ]};
38201     },
38202
38203     getTrigger : function(index){
38204         return this.triggers[index];
38205     },
38206
38207     initTrigger : function(){
38208         var ts = this.trigger.select('.x-form-trigger', true);
38209         this.wrap.setStyle('overflow', 'hidden');
38210         var triggerField = this;
38211         ts.each(function(t, all, index){
38212             t.hide = function(){
38213                 var w = triggerField.wrap.getWidth();
38214                 this.dom.style.display = 'none';
38215                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38216             };
38217             t.show = function(){
38218                 var w = triggerField.wrap.getWidth();
38219                 this.dom.style.display = '';
38220                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38221             };
38222             var triggerIndex = 'Trigger'+(index+1);
38223
38224             if(this['hide'+triggerIndex]){
38225                 t.dom.style.display = 'none';
38226             }
38227             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38228             t.addClassOnOver('x-form-trigger-over');
38229             t.addClassOnClick('x-form-trigger-click');
38230         }, this);
38231         this.triggers = ts.elements;
38232     },
38233
38234     onTrigger1Click : Roo.emptyFn,
38235     onTrigger2Click : Roo.emptyFn
38236 });/*
38237  * Based on:
38238  * Ext JS Library 1.1.1
38239  * Copyright(c) 2006-2007, Ext JS, LLC.
38240  *
38241  * Originally Released Under LGPL - original licence link has changed is not relivant.
38242  *
38243  * Fork - LGPL
38244  * <script type="text/javascript">
38245  */
38246  
38247 /**
38248  * @class Roo.form.TextArea
38249  * @extends Roo.form.TextField
38250  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38251  * support for auto-sizing.
38252  * @constructor
38253  * Creates a new TextArea
38254  * @param {Object} config Configuration options
38255  */
38256 Roo.form.TextArea = function(config){
38257     Roo.form.TextArea.superclass.constructor.call(this, config);
38258     // these are provided exchanges for backwards compat
38259     // minHeight/maxHeight were replaced by growMin/growMax to be
38260     // compatible with TextField growing config values
38261     if(this.minHeight !== undefined){
38262         this.growMin = this.minHeight;
38263     }
38264     if(this.maxHeight !== undefined){
38265         this.growMax = this.maxHeight;
38266     }
38267 };
38268
38269 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38270     /**
38271      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38272      */
38273     growMin : 60,
38274     /**
38275      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38276      */
38277     growMax: 1000,
38278     /**
38279      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38280      * in the field (equivalent to setting overflow: hidden, defaults to false)
38281      */
38282     preventScrollbars: false,
38283     /**
38284      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38285      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38286      */
38287
38288     // private
38289     onRender : function(ct, position){
38290         if(!this.el){
38291             this.defaultAutoCreate = {
38292                 tag: "textarea",
38293                 style:"width:300px;height:60px;",
38294                 autocomplete: "new-password"
38295             };
38296         }
38297         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38298         if(this.grow){
38299             this.textSizeEl = Roo.DomHelper.append(document.body, {
38300                 tag: "pre", cls: "x-form-grow-sizer"
38301             });
38302             if(this.preventScrollbars){
38303                 this.el.setStyle("overflow", "hidden");
38304             }
38305             this.el.setHeight(this.growMin);
38306         }
38307     },
38308
38309     onDestroy : function(){
38310         if(this.textSizeEl){
38311             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38312         }
38313         Roo.form.TextArea.superclass.onDestroy.call(this);
38314     },
38315
38316     // private
38317     onKeyUp : function(e){
38318         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38319             this.autoSize();
38320         }
38321     },
38322
38323     /**
38324      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38325      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38326      */
38327     autoSize : function(){
38328         if(!this.grow || !this.textSizeEl){
38329             return;
38330         }
38331         var el = this.el;
38332         var v = el.dom.value;
38333         var ts = this.textSizeEl;
38334
38335         ts.innerHTML = '';
38336         ts.appendChild(document.createTextNode(v));
38337         v = ts.innerHTML;
38338
38339         Roo.fly(ts).setWidth(this.el.getWidth());
38340         if(v.length < 1){
38341             v = "&#160;&#160;";
38342         }else{
38343             if(Roo.isIE){
38344                 v = v.replace(/\n/g, '<p>&#160;</p>');
38345             }
38346             v += "&#160;\n&#160;";
38347         }
38348         ts.innerHTML = v;
38349         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38350         if(h != this.lastHeight){
38351             this.lastHeight = h;
38352             this.el.setHeight(h);
38353             this.fireEvent("autosize", this, h);
38354         }
38355     }
38356 });/*
38357  * Based on:
38358  * Ext JS Library 1.1.1
38359  * Copyright(c) 2006-2007, Ext JS, LLC.
38360  *
38361  * Originally Released Under LGPL - original licence link has changed is not relivant.
38362  *
38363  * Fork - LGPL
38364  * <script type="text/javascript">
38365  */
38366  
38367
38368 /**
38369  * @class Roo.form.NumberField
38370  * @extends Roo.form.TextField
38371  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38372  * @constructor
38373  * Creates a new NumberField
38374  * @param {Object} config Configuration options
38375  */
38376 Roo.form.NumberField = function(config){
38377     Roo.form.NumberField.superclass.constructor.call(this, config);
38378 };
38379
38380 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38381     /**
38382      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38383      */
38384     fieldClass: "x-form-field x-form-num-field",
38385     /**
38386      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38387      */
38388     allowDecimals : true,
38389     /**
38390      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38391      */
38392     decimalSeparator : ".",
38393     /**
38394      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38395      */
38396     decimalPrecision : 2,
38397     /**
38398      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38399      */
38400     allowNegative : true,
38401     /**
38402      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38403      */
38404     minValue : Number.NEGATIVE_INFINITY,
38405     /**
38406      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38407      */
38408     maxValue : Number.MAX_VALUE,
38409     /**
38410      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38411      */
38412     minText : "The minimum value for this field is {0}",
38413     /**
38414      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38415      */
38416     maxText : "The maximum value for this field is {0}",
38417     /**
38418      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38419      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38420      */
38421     nanText : "{0} is not a valid number",
38422
38423     // private
38424     initEvents : function(){
38425         Roo.form.NumberField.superclass.initEvents.call(this);
38426         var allowed = "0123456789";
38427         if(this.allowDecimals){
38428             allowed += this.decimalSeparator;
38429         }
38430         if(this.allowNegative){
38431             allowed += "-";
38432         }
38433         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38434         var keyPress = function(e){
38435             var k = e.getKey();
38436             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38437                 return;
38438             }
38439             var c = e.getCharCode();
38440             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38441                 e.stopEvent();
38442             }
38443         };
38444         this.el.on("keypress", keyPress, this);
38445     },
38446
38447     // private
38448     validateValue : function(value){
38449         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38450             return false;
38451         }
38452         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38453              return true;
38454         }
38455         var num = this.parseValue(value);
38456         if(isNaN(num)){
38457             this.markInvalid(String.format(this.nanText, value));
38458             return false;
38459         }
38460         if(num < this.minValue){
38461             this.markInvalid(String.format(this.minText, this.minValue));
38462             return false;
38463         }
38464         if(num > this.maxValue){
38465             this.markInvalid(String.format(this.maxText, this.maxValue));
38466             return false;
38467         }
38468         return true;
38469     },
38470
38471     getValue : function(){
38472         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38473     },
38474
38475     // private
38476     parseValue : function(value){
38477         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38478         return isNaN(value) ? '' : value;
38479     },
38480
38481     // private
38482     fixPrecision : function(value){
38483         var nan = isNaN(value);
38484         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38485             return nan ? '' : value;
38486         }
38487         return parseFloat(value).toFixed(this.decimalPrecision);
38488     },
38489
38490     setValue : function(v){
38491         v = this.fixPrecision(v);
38492         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38493     },
38494
38495     // private
38496     decimalPrecisionFcn : function(v){
38497         return Math.floor(v);
38498     },
38499
38500     beforeBlur : function(){
38501         var v = this.parseValue(this.getRawValue());
38502         if(v){
38503             this.setValue(v);
38504         }
38505     }
38506 });/*
38507  * Based on:
38508  * Ext JS Library 1.1.1
38509  * Copyright(c) 2006-2007, Ext JS, LLC.
38510  *
38511  * Originally Released Under LGPL - original licence link has changed is not relivant.
38512  *
38513  * Fork - LGPL
38514  * <script type="text/javascript">
38515  */
38516  
38517 /**
38518  * @class Roo.form.DateField
38519  * @extends Roo.form.TriggerField
38520  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38521 * @constructor
38522 * Create a new DateField
38523 * @param {Object} config
38524  */
38525 Roo.form.DateField = function(config){
38526     Roo.form.DateField.superclass.constructor.call(this, config);
38527     
38528       this.addEvents({
38529          
38530         /**
38531          * @event select
38532          * Fires when a date is selected
38533              * @param {Roo.form.DateField} combo This combo box
38534              * @param {Date} date The date selected
38535              */
38536         'select' : true
38537          
38538     });
38539     
38540     
38541     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38542     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38543     this.ddMatch = null;
38544     if(this.disabledDates){
38545         var dd = this.disabledDates;
38546         var re = "(?:";
38547         for(var i = 0; i < dd.length; i++){
38548             re += dd[i];
38549             if(i != dd.length-1) re += "|";
38550         }
38551         this.ddMatch = new RegExp(re + ")");
38552     }
38553 };
38554
38555 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38556     /**
38557      * @cfg {String} format
38558      * The default date format string which can be overriden for localization support.  The format must be
38559      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38560      */
38561     format : "m/d/y",
38562     /**
38563      * @cfg {String} altFormats
38564      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38565      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38566      */
38567     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38568     /**
38569      * @cfg {Array} disabledDays
38570      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38571      */
38572     disabledDays : null,
38573     /**
38574      * @cfg {String} disabledDaysText
38575      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38576      */
38577     disabledDaysText : "Disabled",
38578     /**
38579      * @cfg {Array} disabledDates
38580      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38581      * expression so they are very powerful. Some examples:
38582      * <ul>
38583      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38584      * <li>["03/08", "09/16"] would disable those days for every year</li>
38585      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38586      * <li>["03/../2006"] would disable every day in March 2006</li>
38587      * <li>["^03"] would disable every day in every March</li>
38588      * </ul>
38589      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38590      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38591      */
38592     disabledDates : null,
38593     /**
38594      * @cfg {String} disabledDatesText
38595      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38596      */
38597     disabledDatesText : "Disabled",
38598     /**
38599      * @cfg {Date/String} minValue
38600      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38601      * valid format (defaults to null).
38602      */
38603     minValue : null,
38604     /**
38605      * @cfg {Date/String} maxValue
38606      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38607      * valid format (defaults to null).
38608      */
38609     maxValue : null,
38610     /**
38611      * @cfg {String} minText
38612      * The error text to display when the date in the cell is before minValue (defaults to
38613      * 'The date in this field must be after {minValue}').
38614      */
38615     minText : "The date in this field must be equal to or after {0}",
38616     /**
38617      * @cfg {String} maxText
38618      * The error text to display when the date in the cell is after maxValue (defaults to
38619      * 'The date in this field must be before {maxValue}').
38620      */
38621     maxText : "The date in this field must be equal to or before {0}",
38622     /**
38623      * @cfg {String} invalidText
38624      * The error text to display when the date in the field is invalid (defaults to
38625      * '{value} is not a valid date - it must be in the format {format}').
38626      */
38627     invalidText : "{0} is not a valid date - it must be in the format {1}",
38628     /**
38629      * @cfg {String} triggerClass
38630      * An additional CSS class used to style the trigger button.  The trigger will always get the
38631      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38632      * which displays a calendar icon).
38633      */
38634     triggerClass : 'x-form-date-trigger',
38635     
38636
38637     /**
38638      * @cfg {Boolean} useIso
38639      * if enabled, then the date field will use a hidden field to store the 
38640      * real value as iso formated date. default (false)
38641      */ 
38642     useIso : false,
38643     /**
38644      * @cfg {String/Object} autoCreate
38645      * A DomHelper element spec, or true for a default element spec (defaults to
38646      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38647      */ 
38648     // private
38649     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38650     
38651     // private
38652     hiddenField: false,
38653     
38654     onRender : function(ct, position)
38655     {
38656         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38657         if (this.useIso) {
38658             //this.el.dom.removeAttribute('name'); 
38659             Roo.log("Changing name?");
38660             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38661             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38662                     'before', true);
38663             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38664             // prevent input submission
38665             this.hiddenName = this.name;
38666         }
38667             
38668             
38669     },
38670     
38671     // private
38672     validateValue : function(value)
38673     {
38674         value = this.formatDate(value);
38675         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38676             Roo.log('super failed');
38677             return false;
38678         }
38679         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38680              return true;
38681         }
38682         var svalue = value;
38683         value = this.parseDate(value);
38684         if(!value){
38685             Roo.log('parse date failed' + svalue);
38686             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38687             return false;
38688         }
38689         var time = value.getTime();
38690         if(this.minValue && time < this.minValue.getTime()){
38691             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38692             return false;
38693         }
38694         if(this.maxValue && time > this.maxValue.getTime()){
38695             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38696             return false;
38697         }
38698         if(this.disabledDays){
38699             var day = value.getDay();
38700             for(var i = 0; i < this.disabledDays.length; i++) {
38701                 if(day === this.disabledDays[i]){
38702                     this.markInvalid(this.disabledDaysText);
38703                     return false;
38704                 }
38705             }
38706         }
38707         var fvalue = this.formatDate(value);
38708         if(this.ddMatch && this.ddMatch.test(fvalue)){
38709             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38710             return false;
38711         }
38712         return true;
38713     },
38714
38715     // private
38716     // Provides logic to override the default TriggerField.validateBlur which just returns true
38717     validateBlur : function(){
38718         return !this.menu || !this.menu.isVisible();
38719     },
38720     
38721     getName: function()
38722     {
38723         // returns hidden if it's set..
38724         if (!this.rendered) {return ''};
38725         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38726         
38727     },
38728
38729     /**
38730      * Returns the current date value of the date field.
38731      * @return {Date} The date value
38732      */
38733     getValue : function(){
38734         
38735         return  this.hiddenField ?
38736                 this.hiddenField.value :
38737                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38738     },
38739
38740     /**
38741      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38742      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38743      * (the default format used is "m/d/y").
38744      * <br />Usage:
38745      * <pre><code>
38746 //All of these calls set the same date value (May 4, 2006)
38747
38748 //Pass a date object:
38749 var dt = new Date('5/4/06');
38750 dateField.setValue(dt);
38751
38752 //Pass a date string (default format):
38753 dateField.setValue('5/4/06');
38754
38755 //Pass a date string (custom format):
38756 dateField.format = 'Y-m-d';
38757 dateField.setValue('2006-5-4');
38758 </code></pre>
38759      * @param {String/Date} date The date or valid date string
38760      */
38761     setValue : function(date){
38762         if (this.hiddenField) {
38763             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38764         }
38765         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38766         // make sure the value field is always stored as a date..
38767         this.value = this.parseDate(date);
38768         
38769         
38770     },
38771
38772     // private
38773     parseDate : function(value){
38774         if(!value || value instanceof Date){
38775             return value;
38776         }
38777         var v = Date.parseDate(value, this.format);
38778          if (!v && this.useIso) {
38779             v = Date.parseDate(value, 'Y-m-d');
38780         }
38781         if(!v && this.altFormats){
38782             if(!this.altFormatsArray){
38783                 this.altFormatsArray = this.altFormats.split("|");
38784             }
38785             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38786                 v = Date.parseDate(value, this.altFormatsArray[i]);
38787             }
38788         }
38789         return v;
38790     },
38791
38792     // private
38793     formatDate : function(date, fmt){
38794         return (!date || !(date instanceof Date)) ?
38795                date : date.dateFormat(fmt || this.format);
38796     },
38797
38798     // private
38799     menuListeners : {
38800         select: function(m, d){
38801             
38802             this.setValue(d);
38803             this.fireEvent('select', this, d);
38804         },
38805         show : function(){ // retain focus styling
38806             this.onFocus();
38807         },
38808         hide : function(){
38809             this.focus.defer(10, this);
38810             var ml = this.menuListeners;
38811             this.menu.un("select", ml.select,  this);
38812             this.menu.un("show", ml.show,  this);
38813             this.menu.un("hide", ml.hide,  this);
38814         }
38815     },
38816
38817     // private
38818     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38819     onTriggerClick : function(){
38820         if(this.disabled){
38821             return;
38822         }
38823         if(this.menu == null){
38824             this.menu = new Roo.menu.DateMenu();
38825         }
38826         Roo.apply(this.menu.picker,  {
38827             showClear: this.allowBlank,
38828             minDate : this.minValue,
38829             maxDate : this.maxValue,
38830             disabledDatesRE : this.ddMatch,
38831             disabledDatesText : this.disabledDatesText,
38832             disabledDays : this.disabledDays,
38833             disabledDaysText : this.disabledDaysText,
38834             format : this.useIso ? 'Y-m-d' : this.format,
38835             minText : String.format(this.minText, this.formatDate(this.minValue)),
38836             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38837         });
38838         this.menu.on(Roo.apply({}, this.menuListeners, {
38839             scope:this
38840         }));
38841         this.menu.picker.setValue(this.getValue() || new Date());
38842         this.menu.show(this.el, "tl-bl?");
38843     },
38844
38845     beforeBlur : function(){
38846         var v = this.parseDate(this.getRawValue());
38847         if(v){
38848             this.setValue(v);
38849         }
38850     },
38851
38852     /*@
38853      * overide
38854      * 
38855      */
38856     isDirty : function() {
38857         if(this.disabled) {
38858             return false;
38859         }
38860         
38861         if(typeof(this.startValue) === 'undefined'){
38862             return false;
38863         }
38864         
38865         return String(this.getValue()) !== String(this.startValue);
38866         
38867     }
38868 });/*
38869  * Based on:
38870  * Ext JS Library 1.1.1
38871  * Copyright(c) 2006-2007, Ext JS, LLC.
38872  *
38873  * Originally Released Under LGPL - original licence link has changed is not relivant.
38874  *
38875  * Fork - LGPL
38876  * <script type="text/javascript">
38877  */
38878  
38879 /**
38880  * @class Roo.form.MonthField
38881  * @extends Roo.form.TriggerField
38882  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38883 * @constructor
38884 * Create a new MonthField
38885 * @param {Object} config
38886  */
38887 Roo.form.MonthField = function(config){
38888     
38889     Roo.form.MonthField.superclass.constructor.call(this, config);
38890     
38891       this.addEvents({
38892          
38893         /**
38894          * @event select
38895          * Fires when a date is selected
38896              * @param {Roo.form.MonthFieeld} combo This combo box
38897              * @param {Date} date The date selected
38898              */
38899         'select' : true
38900          
38901     });
38902     
38903     
38904     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38905     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38906     this.ddMatch = null;
38907     if(this.disabledDates){
38908         var dd = this.disabledDates;
38909         var re = "(?:";
38910         for(var i = 0; i < dd.length; i++){
38911             re += dd[i];
38912             if(i != dd.length-1) re += "|";
38913         }
38914         this.ddMatch = new RegExp(re + ")");
38915     }
38916 };
38917
38918 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38919     /**
38920      * @cfg {String} format
38921      * The default date format string which can be overriden for localization support.  The format must be
38922      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38923      */
38924     format : "M Y",
38925     /**
38926      * @cfg {String} altFormats
38927      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38928      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38929      */
38930     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38931     /**
38932      * @cfg {Array} disabledDays
38933      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38934      */
38935     disabledDays : [0,1,2,3,4,5,6],
38936     /**
38937      * @cfg {String} disabledDaysText
38938      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38939      */
38940     disabledDaysText : "Disabled",
38941     /**
38942      * @cfg {Array} disabledDates
38943      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38944      * expression so they are very powerful. Some examples:
38945      * <ul>
38946      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38947      * <li>["03/08", "09/16"] would disable those days for every year</li>
38948      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38949      * <li>["03/../2006"] would disable every day in March 2006</li>
38950      * <li>["^03"] would disable every day in every March</li>
38951      * </ul>
38952      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38953      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38954      */
38955     disabledDates : null,
38956     /**
38957      * @cfg {String} disabledDatesText
38958      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38959      */
38960     disabledDatesText : "Disabled",
38961     /**
38962      * @cfg {Date/String} minValue
38963      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38964      * valid format (defaults to null).
38965      */
38966     minValue : null,
38967     /**
38968      * @cfg {Date/String} maxValue
38969      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38970      * valid format (defaults to null).
38971      */
38972     maxValue : null,
38973     /**
38974      * @cfg {String} minText
38975      * The error text to display when the date in the cell is before minValue (defaults to
38976      * 'The date in this field must be after {minValue}').
38977      */
38978     minText : "The date in this field must be equal to or after {0}",
38979     /**
38980      * @cfg {String} maxTextf
38981      * The error text to display when the date in the cell is after maxValue (defaults to
38982      * 'The date in this field must be before {maxValue}').
38983      */
38984     maxText : "The date in this field must be equal to or before {0}",
38985     /**
38986      * @cfg {String} invalidText
38987      * The error text to display when the date in the field is invalid (defaults to
38988      * '{value} is not a valid date - it must be in the format {format}').
38989      */
38990     invalidText : "{0} is not a valid date - it must be in the format {1}",
38991     /**
38992      * @cfg {String} triggerClass
38993      * An additional CSS class used to style the trigger button.  The trigger will always get the
38994      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38995      * which displays a calendar icon).
38996      */
38997     triggerClass : 'x-form-date-trigger',
38998     
38999
39000     /**
39001      * @cfg {Boolean} useIso
39002      * if enabled, then the date field will use a hidden field to store the 
39003      * real value as iso formated date. default (true)
39004      */ 
39005     useIso : true,
39006     /**
39007      * @cfg {String/Object} autoCreate
39008      * A DomHelper element spec, or true for a default element spec (defaults to
39009      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
39010      */ 
39011     // private
39012     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
39013     
39014     // private
39015     hiddenField: false,
39016     
39017     hideMonthPicker : false,
39018     
39019     onRender : function(ct, position)
39020     {
39021         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39022         if (this.useIso) {
39023             this.el.dom.removeAttribute('name'); 
39024             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39025                     'before', true);
39026             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39027             // prevent input submission
39028             this.hiddenName = this.name;
39029         }
39030             
39031             
39032     },
39033     
39034     // private
39035     validateValue : function(value)
39036     {
39037         value = this.formatDate(value);
39038         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39039             return false;
39040         }
39041         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39042              return true;
39043         }
39044         var svalue = value;
39045         value = this.parseDate(value);
39046         if(!value){
39047             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39048             return false;
39049         }
39050         var time = value.getTime();
39051         if(this.minValue && time < this.minValue.getTime()){
39052             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39053             return false;
39054         }
39055         if(this.maxValue && time > this.maxValue.getTime()){
39056             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39057             return false;
39058         }
39059         /*if(this.disabledDays){
39060             var day = value.getDay();
39061             for(var i = 0; i < this.disabledDays.length; i++) {
39062                 if(day === this.disabledDays[i]){
39063                     this.markInvalid(this.disabledDaysText);
39064                     return false;
39065                 }
39066             }
39067         }
39068         */
39069         var fvalue = this.formatDate(value);
39070         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39071             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39072             return false;
39073         }
39074         */
39075         return true;
39076     },
39077
39078     // private
39079     // Provides logic to override the default TriggerField.validateBlur which just returns true
39080     validateBlur : function(){
39081         return !this.menu || !this.menu.isVisible();
39082     },
39083
39084     /**
39085      * Returns the current date value of the date field.
39086      * @return {Date} The date value
39087      */
39088     getValue : function(){
39089         
39090         
39091         
39092         return  this.hiddenField ?
39093                 this.hiddenField.value :
39094                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39095     },
39096
39097     /**
39098      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39099      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39100      * (the default format used is "m/d/y").
39101      * <br />Usage:
39102      * <pre><code>
39103 //All of these calls set the same date value (May 4, 2006)
39104
39105 //Pass a date object:
39106 var dt = new Date('5/4/06');
39107 monthField.setValue(dt);
39108
39109 //Pass a date string (default format):
39110 monthField.setValue('5/4/06');
39111
39112 //Pass a date string (custom format):
39113 monthField.format = 'Y-m-d';
39114 monthField.setValue('2006-5-4');
39115 </code></pre>
39116      * @param {String/Date} date The date or valid date string
39117      */
39118     setValue : function(date){
39119         Roo.log('month setValue' + date);
39120         // can only be first of month..
39121         
39122         var val = this.parseDate(date);
39123         
39124         if (this.hiddenField) {
39125             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39126         }
39127         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39128         this.value = this.parseDate(date);
39129     },
39130
39131     // private
39132     parseDate : function(value){
39133         if(!value || value instanceof Date){
39134             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39135             return value;
39136         }
39137         var v = Date.parseDate(value, this.format);
39138         if (!v && this.useIso) {
39139             v = Date.parseDate(value, 'Y-m-d');
39140         }
39141         if (v) {
39142             // 
39143             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39144         }
39145         
39146         
39147         if(!v && this.altFormats){
39148             if(!this.altFormatsArray){
39149                 this.altFormatsArray = this.altFormats.split("|");
39150             }
39151             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39152                 v = Date.parseDate(value, this.altFormatsArray[i]);
39153             }
39154         }
39155         return v;
39156     },
39157
39158     // private
39159     formatDate : function(date, fmt){
39160         return (!date || !(date instanceof Date)) ?
39161                date : date.dateFormat(fmt || this.format);
39162     },
39163
39164     // private
39165     menuListeners : {
39166         select: function(m, d){
39167             this.setValue(d);
39168             this.fireEvent('select', this, d);
39169         },
39170         show : function(){ // retain focus styling
39171             this.onFocus();
39172         },
39173         hide : function(){
39174             this.focus.defer(10, this);
39175             var ml = this.menuListeners;
39176             this.menu.un("select", ml.select,  this);
39177             this.menu.un("show", ml.show,  this);
39178             this.menu.un("hide", ml.hide,  this);
39179         }
39180     },
39181     // private
39182     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39183     onTriggerClick : function(){
39184         if(this.disabled){
39185             return;
39186         }
39187         if(this.menu == null){
39188             this.menu = new Roo.menu.DateMenu();
39189            
39190         }
39191         
39192         Roo.apply(this.menu.picker,  {
39193             
39194             showClear: this.allowBlank,
39195             minDate : this.minValue,
39196             maxDate : this.maxValue,
39197             disabledDatesRE : this.ddMatch,
39198             disabledDatesText : this.disabledDatesText,
39199             
39200             format : this.useIso ? 'Y-m-d' : this.format,
39201             minText : String.format(this.minText, this.formatDate(this.minValue)),
39202             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39203             
39204         });
39205          this.menu.on(Roo.apply({}, this.menuListeners, {
39206             scope:this
39207         }));
39208        
39209         
39210         var m = this.menu;
39211         var p = m.picker;
39212         
39213         // hide month picker get's called when we called by 'before hide';
39214         
39215         var ignorehide = true;
39216         p.hideMonthPicker  = function(disableAnim){
39217             if (ignorehide) {
39218                 return;
39219             }
39220              if(this.monthPicker){
39221                 Roo.log("hideMonthPicker called");
39222                 if(disableAnim === true){
39223                     this.monthPicker.hide();
39224                 }else{
39225                     this.monthPicker.slideOut('t', {duration:.2});
39226                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39227                     p.fireEvent("select", this, this.value);
39228                     m.hide();
39229                 }
39230             }
39231         }
39232         
39233         Roo.log('picker set value');
39234         Roo.log(this.getValue());
39235         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39236         m.show(this.el, 'tl-bl?');
39237         ignorehide  = false;
39238         // this will trigger hideMonthPicker..
39239         
39240         
39241         // hidden the day picker
39242         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39243         
39244         
39245         
39246       
39247         
39248         p.showMonthPicker.defer(100, p);
39249     
39250         
39251        
39252     },
39253
39254     beforeBlur : function(){
39255         var v = this.parseDate(this.getRawValue());
39256         if(v){
39257             this.setValue(v);
39258         }
39259     }
39260
39261     /** @cfg {Boolean} grow @hide */
39262     /** @cfg {Number} growMin @hide */
39263     /** @cfg {Number} growMax @hide */
39264     /**
39265      * @hide
39266      * @method autoSize
39267      */
39268 });/*
39269  * Based on:
39270  * Ext JS Library 1.1.1
39271  * Copyright(c) 2006-2007, Ext JS, LLC.
39272  *
39273  * Originally Released Under LGPL - original licence link has changed is not relivant.
39274  *
39275  * Fork - LGPL
39276  * <script type="text/javascript">
39277  */
39278  
39279
39280 /**
39281  * @class Roo.form.ComboBox
39282  * @extends Roo.form.TriggerField
39283  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39284  * @constructor
39285  * Create a new ComboBox.
39286  * @param {Object} config Configuration options
39287  */
39288 Roo.form.ComboBox = function(config){
39289     Roo.form.ComboBox.superclass.constructor.call(this, config);
39290     this.addEvents({
39291         /**
39292          * @event expand
39293          * Fires when the dropdown list is expanded
39294              * @param {Roo.form.ComboBox} combo This combo box
39295              */
39296         'expand' : true,
39297         /**
39298          * @event collapse
39299          * Fires when the dropdown list is collapsed
39300              * @param {Roo.form.ComboBox} combo This combo box
39301              */
39302         'collapse' : true,
39303         /**
39304          * @event beforeselect
39305          * Fires before a list item is selected. Return false to cancel the selection.
39306              * @param {Roo.form.ComboBox} combo This combo box
39307              * @param {Roo.data.Record} record The data record returned from the underlying store
39308              * @param {Number} index The index of the selected item in the dropdown list
39309              */
39310         'beforeselect' : true,
39311         /**
39312          * @event select
39313          * Fires when a list item is selected
39314              * @param {Roo.form.ComboBox} combo This combo box
39315              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39316              * @param {Number} index The index of the selected item in the dropdown list
39317              */
39318         'select' : true,
39319         /**
39320          * @event beforequery
39321          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39322          * The event object passed has these properties:
39323              * @param {Roo.form.ComboBox} combo This combo box
39324              * @param {String} query The query
39325              * @param {Boolean} forceAll true to force "all" query
39326              * @param {Boolean} cancel true to cancel the query
39327              * @param {Object} e The query event object
39328              */
39329         'beforequery': true,
39330          /**
39331          * @event add
39332          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39333              * @param {Roo.form.ComboBox} combo This combo box
39334              */
39335         'add' : true,
39336         /**
39337          * @event edit
39338          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39339              * @param {Roo.form.ComboBox} combo This combo box
39340              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39341              */
39342         'edit' : true
39343         
39344         
39345     });
39346     if(this.transform){
39347         this.allowDomMove = false;
39348         var s = Roo.getDom(this.transform);
39349         if(!this.hiddenName){
39350             this.hiddenName = s.name;
39351         }
39352         if(!this.store){
39353             this.mode = 'local';
39354             var d = [], opts = s.options;
39355             for(var i = 0, len = opts.length;i < len; i++){
39356                 var o = opts[i];
39357                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39358                 if(o.selected) {
39359                     this.value = value;
39360                 }
39361                 d.push([value, o.text]);
39362             }
39363             this.store = new Roo.data.SimpleStore({
39364                 'id': 0,
39365                 fields: ['value', 'text'],
39366                 data : d
39367             });
39368             this.valueField = 'value';
39369             this.displayField = 'text';
39370         }
39371         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39372         if(!this.lazyRender){
39373             this.target = true;
39374             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39375             s.parentNode.removeChild(s); // remove it
39376             this.render(this.el.parentNode);
39377         }else{
39378             s.parentNode.removeChild(s); // remove it
39379         }
39380
39381     }
39382     if (this.store) {
39383         this.store = Roo.factory(this.store, Roo.data);
39384     }
39385     
39386     this.selectedIndex = -1;
39387     if(this.mode == 'local'){
39388         if(config.queryDelay === undefined){
39389             this.queryDelay = 10;
39390         }
39391         if(config.minChars === undefined){
39392             this.minChars = 0;
39393         }
39394     }
39395 };
39396
39397 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39398     /**
39399      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39400      */
39401     /**
39402      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39403      * rendering into an Roo.Editor, defaults to false)
39404      */
39405     /**
39406      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39407      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39408      */
39409     /**
39410      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39411      */
39412     /**
39413      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39414      * the dropdown list (defaults to undefined, with no header element)
39415      */
39416
39417      /**
39418      * @cfg {String/Roo.Template} tpl The template to use to render the output
39419      */
39420      
39421     // private
39422     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39423     /**
39424      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39425      */
39426     listWidth: undefined,
39427     /**
39428      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39429      * mode = 'remote' or 'text' if mode = 'local')
39430      */
39431     displayField: undefined,
39432     /**
39433      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39434      * mode = 'remote' or 'value' if mode = 'local'). 
39435      * Note: use of a valueField requires the user make a selection
39436      * in order for a value to be mapped.
39437      */
39438     valueField: undefined,
39439     
39440     
39441     /**
39442      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39443      * field's data value (defaults to the underlying DOM element's name)
39444      */
39445     hiddenName: undefined,
39446     /**
39447      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39448      */
39449     listClass: '',
39450     /**
39451      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39452      */
39453     selectedClass: 'x-combo-selected',
39454     /**
39455      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39456      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39457      * which displays a downward arrow icon).
39458      */
39459     triggerClass : 'x-form-arrow-trigger',
39460     /**
39461      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39462      */
39463     shadow:'sides',
39464     /**
39465      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39466      * anchor positions (defaults to 'tl-bl')
39467      */
39468     listAlign: 'tl-bl?',
39469     /**
39470      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39471      */
39472     maxHeight: 300,
39473     /**
39474      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39475      * query specified by the allQuery config option (defaults to 'query')
39476      */
39477     triggerAction: 'query',
39478     /**
39479      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39480      * (defaults to 4, does not apply if editable = false)
39481      */
39482     minChars : 4,
39483     /**
39484      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39485      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39486      */
39487     typeAhead: false,
39488     /**
39489      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39490      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39491      */
39492     queryDelay: 500,
39493     /**
39494      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39495      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39496      */
39497     pageSize: 0,
39498     /**
39499      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39500      * when editable = true (defaults to false)
39501      */
39502     selectOnFocus:false,
39503     /**
39504      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39505      */
39506     queryParam: 'query',
39507     /**
39508      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39509      * when mode = 'remote' (defaults to 'Loading...')
39510      */
39511     loadingText: 'Loading...',
39512     /**
39513      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39514      */
39515     resizable: false,
39516     /**
39517      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39518      */
39519     handleHeight : 8,
39520     /**
39521      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39522      * traditional select (defaults to true)
39523      */
39524     editable: true,
39525     /**
39526      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39527      */
39528     allQuery: '',
39529     /**
39530      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39531      */
39532     mode: 'remote',
39533     /**
39534      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39535      * listWidth has a higher value)
39536      */
39537     minListWidth : 70,
39538     /**
39539      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39540      * allow the user to set arbitrary text into the field (defaults to false)
39541      */
39542     forceSelection:false,
39543     /**
39544      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39545      * if typeAhead = true (defaults to 250)
39546      */
39547     typeAheadDelay : 250,
39548     /**
39549      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39550      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39551      */
39552     valueNotFoundText : undefined,
39553     /**
39554      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39555      */
39556     blockFocus : false,
39557     
39558     /**
39559      * @cfg {Boolean} disableClear Disable showing of clear button.
39560      */
39561     disableClear : false,
39562     /**
39563      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39564      */
39565     alwaysQuery : false,
39566     
39567     //private
39568     addicon : false,
39569     editicon: false,
39570     
39571     // element that contains real text value.. (when hidden is used..)
39572      
39573     // private
39574     onRender : function(ct, position){
39575         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39576         if(this.hiddenName){
39577             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39578                     'before', true);
39579             this.hiddenField.value =
39580                 this.hiddenValue !== undefined ? this.hiddenValue :
39581                 this.value !== undefined ? this.value : '';
39582
39583             // prevent input submission
39584             this.el.dom.removeAttribute('name');
39585              
39586              
39587         }
39588         if(Roo.isGecko){
39589             this.el.dom.setAttribute('autocomplete', 'off');
39590         }
39591
39592         var cls = 'x-combo-list';
39593
39594         this.list = new Roo.Layer({
39595             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39596         });
39597
39598         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39599         this.list.setWidth(lw);
39600         this.list.swallowEvent('mousewheel');
39601         this.assetHeight = 0;
39602
39603         if(this.title){
39604             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39605             this.assetHeight += this.header.getHeight();
39606         }
39607
39608         this.innerList = this.list.createChild({cls:cls+'-inner'});
39609         this.innerList.on('mouseover', this.onViewOver, this);
39610         this.innerList.on('mousemove', this.onViewMove, this);
39611         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39612         
39613         if(this.allowBlank && !this.pageSize && !this.disableClear){
39614             this.footer = this.list.createChild({cls:cls+'-ft'});
39615             this.pageTb = new Roo.Toolbar(this.footer);
39616            
39617         }
39618         if(this.pageSize){
39619             this.footer = this.list.createChild({cls:cls+'-ft'});
39620             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39621                     {pageSize: this.pageSize});
39622             
39623         }
39624         
39625         if (this.pageTb && this.allowBlank && !this.disableClear) {
39626             var _this = this;
39627             this.pageTb.add(new Roo.Toolbar.Fill(), {
39628                 cls: 'x-btn-icon x-btn-clear',
39629                 text: '&#160;',
39630                 handler: function()
39631                 {
39632                     _this.collapse();
39633                     _this.clearValue();
39634                     _this.onSelect(false, -1);
39635                 }
39636             });
39637         }
39638         if (this.footer) {
39639             this.assetHeight += this.footer.getHeight();
39640         }
39641         
39642
39643         if(!this.tpl){
39644             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39645         }
39646
39647         this.view = new Roo.View(this.innerList, this.tpl, {
39648             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39649         });
39650
39651         this.view.on('click', this.onViewClick, this);
39652
39653         this.store.on('beforeload', this.onBeforeLoad, this);
39654         this.store.on('load', this.onLoad, this);
39655         this.store.on('loadexception', this.onLoadException, this);
39656
39657         if(this.resizable){
39658             this.resizer = new Roo.Resizable(this.list,  {
39659                pinned:true, handles:'se'
39660             });
39661             this.resizer.on('resize', function(r, w, h){
39662                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39663                 this.listWidth = w;
39664                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39665                 this.restrictHeight();
39666             }, this);
39667             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39668         }
39669         if(!this.editable){
39670             this.editable = true;
39671             this.setEditable(false);
39672         }  
39673         
39674         
39675         if (typeof(this.events.add.listeners) != 'undefined') {
39676             
39677             this.addicon = this.wrap.createChild(
39678                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39679        
39680             this.addicon.on('click', function(e) {
39681                 this.fireEvent('add', this);
39682             }, this);
39683         }
39684         if (typeof(this.events.edit.listeners) != 'undefined') {
39685             
39686             this.editicon = this.wrap.createChild(
39687                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39688             if (this.addicon) {
39689                 this.editicon.setStyle('margin-left', '40px');
39690             }
39691             this.editicon.on('click', function(e) {
39692                 
39693                 // we fire even  if inothing is selected..
39694                 this.fireEvent('edit', this, this.lastData );
39695                 
39696             }, this);
39697         }
39698         
39699         
39700         
39701     },
39702
39703     // private
39704     initEvents : function(){
39705         Roo.form.ComboBox.superclass.initEvents.call(this);
39706
39707         this.keyNav = new Roo.KeyNav(this.el, {
39708             "up" : function(e){
39709                 this.inKeyMode = true;
39710                 this.selectPrev();
39711             },
39712
39713             "down" : function(e){
39714                 if(!this.isExpanded()){
39715                     this.onTriggerClick();
39716                 }else{
39717                     this.inKeyMode = true;
39718                     this.selectNext();
39719                 }
39720             },
39721
39722             "enter" : function(e){
39723                 this.onViewClick();
39724                 //return true;
39725             },
39726
39727             "esc" : function(e){
39728                 this.collapse();
39729             },
39730
39731             "tab" : function(e){
39732                 this.onViewClick(false);
39733                 this.fireEvent("specialkey", this, e);
39734                 return true;
39735             },
39736
39737             scope : this,
39738
39739             doRelay : function(foo, bar, hname){
39740                 if(hname == 'down' || this.scope.isExpanded()){
39741                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39742                 }
39743                 return true;
39744             },
39745
39746             forceKeyDown: true
39747         });
39748         this.queryDelay = Math.max(this.queryDelay || 10,
39749                 this.mode == 'local' ? 10 : 250);
39750         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39751         if(this.typeAhead){
39752             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39753         }
39754         if(this.editable !== false){
39755             this.el.on("keyup", this.onKeyUp, this);
39756         }
39757         if(this.forceSelection){
39758             this.on('blur', this.doForce, this);
39759         }
39760     },
39761
39762     onDestroy : function(){
39763         if(this.view){
39764             this.view.setStore(null);
39765             this.view.el.removeAllListeners();
39766             this.view.el.remove();
39767             this.view.purgeListeners();
39768         }
39769         if(this.list){
39770             this.list.destroy();
39771         }
39772         if(this.store){
39773             this.store.un('beforeload', this.onBeforeLoad, this);
39774             this.store.un('load', this.onLoad, this);
39775             this.store.un('loadexception', this.onLoadException, this);
39776         }
39777         Roo.form.ComboBox.superclass.onDestroy.call(this);
39778     },
39779
39780     // private
39781     fireKey : function(e){
39782         if(e.isNavKeyPress() && !this.list.isVisible()){
39783             this.fireEvent("specialkey", this, e);
39784         }
39785     },
39786
39787     // private
39788     onResize: function(w, h){
39789         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39790         
39791         if(typeof w != 'number'){
39792             // we do not handle it!?!?
39793             return;
39794         }
39795         var tw = this.trigger.getWidth();
39796         tw += this.addicon ? this.addicon.getWidth() : 0;
39797         tw += this.editicon ? this.editicon.getWidth() : 0;
39798         var x = w - tw;
39799         this.el.setWidth( this.adjustWidth('input', x));
39800             
39801         this.trigger.setStyle('left', x+'px');
39802         
39803         if(this.list && this.listWidth === undefined){
39804             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39805             this.list.setWidth(lw);
39806             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39807         }
39808         
39809     
39810         
39811     },
39812
39813     /**
39814      * Allow or prevent the user from directly editing the field text.  If false is passed,
39815      * the user will only be able to select from the items defined in the dropdown list.  This method
39816      * is the runtime equivalent of setting the 'editable' config option at config time.
39817      * @param {Boolean} value True to allow the user to directly edit the field text
39818      */
39819     setEditable : function(value){
39820         if(value == this.editable){
39821             return;
39822         }
39823         this.editable = value;
39824         if(!value){
39825             this.el.dom.setAttribute('readOnly', true);
39826             this.el.on('mousedown', this.onTriggerClick,  this);
39827             this.el.addClass('x-combo-noedit');
39828         }else{
39829             this.el.dom.setAttribute('readOnly', false);
39830             this.el.un('mousedown', this.onTriggerClick,  this);
39831             this.el.removeClass('x-combo-noedit');
39832         }
39833     },
39834
39835     // private
39836     onBeforeLoad : function(){
39837         if(!this.hasFocus){
39838             return;
39839         }
39840         this.innerList.update(this.loadingText ?
39841                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39842         this.restrictHeight();
39843         this.selectedIndex = -1;
39844     },
39845
39846     // private
39847     onLoad : function(){
39848         if(!this.hasFocus){
39849             return;
39850         }
39851         if(this.store.getCount() > 0){
39852             this.expand();
39853             this.restrictHeight();
39854             if(this.lastQuery == this.allQuery){
39855                 if(this.editable){
39856                     this.el.dom.select();
39857                 }
39858                 if(!this.selectByValue(this.value, true)){
39859                     this.select(0, true);
39860                 }
39861             }else{
39862                 this.selectNext();
39863                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39864                     this.taTask.delay(this.typeAheadDelay);
39865                 }
39866             }
39867         }else{
39868             this.onEmptyResults();
39869         }
39870         //this.el.focus();
39871     },
39872     // private
39873     onLoadException : function()
39874     {
39875         this.collapse();
39876         Roo.log(this.store.reader.jsonData);
39877         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39878             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39879         }
39880         
39881         
39882     },
39883     // private
39884     onTypeAhead : function(){
39885         if(this.store.getCount() > 0){
39886             var r = this.store.getAt(0);
39887             var newValue = r.data[this.displayField];
39888             var len = newValue.length;
39889             var selStart = this.getRawValue().length;
39890             if(selStart != len){
39891                 this.setRawValue(newValue);
39892                 this.selectText(selStart, newValue.length);
39893             }
39894         }
39895     },
39896
39897     // private
39898     onSelect : function(record, index){
39899         if(this.fireEvent('beforeselect', this, record, index) !== false){
39900             this.setFromData(index > -1 ? record.data : false);
39901             this.collapse();
39902             this.fireEvent('select', this, record, index);
39903         }
39904     },
39905
39906     /**
39907      * Returns the currently selected field value or empty string if no value is set.
39908      * @return {String} value The selected value
39909      */
39910     getValue : function(){
39911         if(this.valueField){
39912             return typeof this.value != 'undefined' ? this.value : '';
39913         }
39914         return Roo.form.ComboBox.superclass.getValue.call(this);
39915     },
39916
39917     /**
39918      * Clears any text/value currently set in the field
39919      */
39920     clearValue : function(){
39921         if(this.hiddenField){
39922             this.hiddenField.value = '';
39923         }
39924         this.value = '';
39925         this.setRawValue('');
39926         this.lastSelectionText = '';
39927         
39928     },
39929
39930     /**
39931      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39932      * will be displayed in the field.  If the value does not match the data value of an existing item,
39933      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39934      * Otherwise the field will be blank (although the value will still be set).
39935      * @param {String} value The value to match
39936      */
39937     setValue : function(v){
39938         var text = v;
39939         if(this.valueField){
39940             var r = this.findRecord(this.valueField, v);
39941             if(r){
39942                 text = r.data[this.displayField];
39943             }else if(this.valueNotFoundText !== undefined){
39944                 text = this.valueNotFoundText;
39945             }
39946         }
39947         this.lastSelectionText = text;
39948         if(this.hiddenField){
39949             this.hiddenField.value = v;
39950         }
39951         Roo.form.ComboBox.superclass.setValue.call(this, text);
39952         this.value = v;
39953     },
39954     /**
39955      * @property {Object} the last set data for the element
39956      */
39957     
39958     lastData : false,
39959     /**
39960      * Sets the value of the field based on a object which is related to the record format for the store.
39961      * @param {Object} value the value to set as. or false on reset?
39962      */
39963     setFromData : function(o){
39964         var dv = ''; // display value
39965         var vv = ''; // value value..
39966         this.lastData = o;
39967         if (this.displayField) {
39968             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39969         } else {
39970             // this is an error condition!!!
39971             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39972         }
39973         
39974         if(this.valueField){
39975             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39976         }
39977         if(this.hiddenField){
39978             this.hiddenField.value = vv;
39979             
39980             this.lastSelectionText = dv;
39981             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39982             this.value = vv;
39983             return;
39984         }
39985         // no hidden field.. - we store the value in 'value', but still display
39986         // display field!!!!
39987         this.lastSelectionText = dv;
39988         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39989         this.value = vv;
39990         
39991         
39992     },
39993     // private
39994     reset : function(){
39995         // overridden so that last data is reset..
39996         this.setValue(this.resetValue);
39997         this.clearInvalid();
39998         this.lastData = false;
39999         if (this.view) {
40000             this.view.clearSelections();
40001         }
40002     },
40003     // private
40004     findRecord : function(prop, value){
40005         var record;
40006         if(this.store.getCount() > 0){
40007             this.store.each(function(r){
40008                 if(r.data[prop] == value){
40009                     record = r;
40010                     return false;
40011                 }
40012                 return true;
40013             });
40014         }
40015         return record;
40016     },
40017     
40018     getName: function()
40019     {
40020         // returns hidden if it's set..
40021         if (!this.rendered) {return ''};
40022         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40023         
40024     },
40025     // private
40026     onViewMove : function(e, t){
40027         this.inKeyMode = false;
40028     },
40029
40030     // private
40031     onViewOver : function(e, t){
40032         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40033             return;
40034         }
40035         var item = this.view.findItemFromChild(t);
40036         if(item){
40037             var index = this.view.indexOf(item);
40038             this.select(index, false);
40039         }
40040     },
40041
40042     // private
40043     onViewClick : function(doFocus)
40044     {
40045         var index = this.view.getSelectedIndexes()[0];
40046         var r = this.store.getAt(index);
40047         if(r){
40048             this.onSelect(r, index);
40049         }
40050         if(doFocus !== false && !this.blockFocus){
40051             this.el.focus();
40052         }
40053     },
40054
40055     // private
40056     restrictHeight : function(){
40057         this.innerList.dom.style.height = '';
40058         var inner = this.innerList.dom;
40059         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40060         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40061         this.list.beginUpdate();
40062         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40063         this.list.alignTo(this.el, this.listAlign);
40064         this.list.endUpdate();
40065     },
40066
40067     // private
40068     onEmptyResults : function(){
40069         this.collapse();
40070     },
40071
40072     /**
40073      * Returns true if the dropdown list is expanded, else false.
40074      */
40075     isExpanded : function(){
40076         return this.list.isVisible();
40077     },
40078
40079     /**
40080      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40081      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40082      * @param {String} value The data value of the item to select
40083      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40084      * selected item if it is not currently in view (defaults to true)
40085      * @return {Boolean} True if the value matched an item in the list, else false
40086      */
40087     selectByValue : function(v, scrollIntoView){
40088         if(v !== undefined && v !== null){
40089             var r = this.findRecord(this.valueField || this.displayField, v);
40090             if(r){
40091                 this.select(this.store.indexOf(r), scrollIntoView);
40092                 return true;
40093             }
40094         }
40095         return false;
40096     },
40097
40098     /**
40099      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40100      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40101      * @param {Number} index The zero-based index of the list item to select
40102      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40103      * selected item if it is not currently in view (defaults to true)
40104      */
40105     select : function(index, scrollIntoView){
40106         this.selectedIndex = index;
40107         this.view.select(index);
40108         if(scrollIntoView !== false){
40109             var el = this.view.getNode(index);
40110             if(el){
40111                 this.innerList.scrollChildIntoView(el, false);
40112             }
40113         }
40114     },
40115
40116     // private
40117     selectNext : function(){
40118         var ct = this.store.getCount();
40119         if(ct > 0){
40120             if(this.selectedIndex == -1){
40121                 this.select(0);
40122             }else if(this.selectedIndex < ct-1){
40123                 this.select(this.selectedIndex+1);
40124             }
40125         }
40126     },
40127
40128     // private
40129     selectPrev : function(){
40130         var ct = this.store.getCount();
40131         if(ct > 0){
40132             if(this.selectedIndex == -1){
40133                 this.select(0);
40134             }else if(this.selectedIndex != 0){
40135                 this.select(this.selectedIndex-1);
40136             }
40137         }
40138     },
40139
40140     // private
40141     onKeyUp : function(e){
40142         if(this.editable !== false && !e.isSpecialKey()){
40143             this.lastKey = e.getKey();
40144             this.dqTask.delay(this.queryDelay);
40145         }
40146     },
40147
40148     // private
40149     validateBlur : function(){
40150         return !this.list || !this.list.isVisible();   
40151     },
40152
40153     // private
40154     initQuery : function(){
40155         this.doQuery(this.getRawValue());
40156     },
40157
40158     // private
40159     doForce : function(){
40160         if(this.el.dom.value.length > 0){
40161             this.el.dom.value =
40162                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40163              
40164         }
40165     },
40166
40167     /**
40168      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40169      * query allowing the query action to be canceled if needed.
40170      * @param {String} query The SQL query to execute
40171      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40172      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40173      * saved in the current store (defaults to false)
40174      */
40175     doQuery : function(q, forceAll){
40176         if(q === undefined || q === null){
40177             q = '';
40178         }
40179         var qe = {
40180             query: q,
40181             forceAll: forceAll,
40182             combo: this,
40183             cancel:false
40184         };
40185         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40186             return false;
40187         }
40188         q = qe.query;
40189         forceAll = qe.forceAll;
40190         if(forceAll === true || (q.length >= this.minChars)){
40191             if(this.lastQuery != q || this.alwaysQuery){
40192                 this.lastQuery = q;
40193                 if(this.mode == 'local'){
40194                     this.selectedIndex = -1;
40195                     if(forceAll){
40196                         this.store.clearFilter();
40197                     }else{
40198                         this.store.filter(this.displayField, q);
40199                     }
40200                     this.onLoad();
40201                 }else{
40202                     this.store.baseParams[this.queryParam] = q;
40203                     this.store.load({
40204                         params: this.getParams(q)
40205                     });
40206                     this.expand();
40207                 }
40208             }else{
40209                 this.selectedIndex = -1;
40210                 this.onLoad();   
40211             }
40212         }
40213     },
40214
40215     // private
40216     getParams : function(q){
40217         var p = {};
40218         //p[this.queryParam] = q;
40219         if(this.pageSize){
40220             p.start = 0;
40221             p.limit = this.pageSize;
40222         }
40223         return p;
40224     },
40225
40226     /**
40227      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40228      */
40229     collapse : function(){
40230         if(!this.isExpanded()){
40231             return;
40232         }
40233         this.list.hide();
40234         Roo.get(document).un('mousedown', this.collapseIf, this);
40235         Roo.get(document).un('mousewheel', this.collapseIf, this);
40236         if (!this.editable) {
40237             Roo.get(document).un('keydown', this.listKeyPress, this);
40238         }
40239         this.fireEvent('collapse', this);
40240     },
40241
40242     // private
40243     collapseIf : function(e){
40244         if(!e.within(this.wrap) && !e.within(this.list)){
40245             this.collapse();
40246         }
40247     },
40248
40249     /**
40250      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40251      */
40252     expand : function(){
40253         if(this.isExpanded() || !this.hasFocus){
40254             return;
40255         }
40256         this.list.alignTo(this.el, this.listAlign);
40257         this.list.show();
40258         Roo.get(document).on('mousedown', this.collapseIf, this);
40259         Roo.get(document).on('mousewheel', this.collapseIf, this);
40260         if (!this.editable) {
40261             Roo.get(document).on('keydown', this.listKeyPress, this);
40262         }
40263         
40264         this.fireEvent('expand', this);
40265     },
40266
40267     // private
40268     // Implements the default empty TriggerField.onTriggerClick function
40269     onTriggerClick : function(){
40270         if(this.disabled){
40271             return;
40272         }
40273         if(this.isExpanded()){
40274             this.collapse();
40275             if (!this.blockFocus) {
40276                 this.el.focus();
40277             }
40278             
40279         }else {
40280             this.hasFocus = true;
40281             if(this.triggerAction == 'all') {
40282                 this.doQuery(this.allQuery, true);
40283             } else {
40284                 this.doQuery(this.getRawValue());
40285             }
40286             if (!this.blockFocus) {
40287                 this.el.focus();
40288             }
40289         }
40290     },
40291     listKeyPress : function(e)
40292     {
40293         //Roo.log('listkeypress');
40294         // scroll to first matching element based on key pres..
40295         if (e.isSpecialKey()) {
40296             return false;
40297         }
40298         var k = String.fromCharCode(e.getKey()).toUpperCase();
40299         //Roo.log(k);
40300         var match  = false;
40301         var csel = this.view.getSelectedNodes();
40302         var cselitem = false;
40303         if (csel.length) {
40304             var ix = this.view.indexOf(csel[0]);
40305             cselitem  = this.store.getAt(ix);
40306             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40307                 cselitem = false;
40308             }
40309             
40310         }
40311         
40312         this.store.each(function(v) { 
40313             if (cselitem) {
40314                 // start at existing selection.
40315                 if (cselitem.id == v.id) {
40316                     cselitem = false;
40317                 }
40318                 return;
40319             }
40320                 
40321             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40322                 match = this.store.indexOf(v);
40323                 return false;
40324             }
40325         }, this);
40326         
40327         if (match === false) {
40328             return true; // no more action?
40329         }
40330         // scroll to?
40331         this.view.select(match);
40332         var sn = Roo.get(this.view.getSelectedNodes()[0])
40333         sn.scrollIntoView(sn.dom.parentNode, false);
40334     }
40335
40336     /** 
40337     * @cfg {Boolean} grow 
40338     * @hide 
40339     */
40340     /** 
40341     * @cfg {Number} growMin 
40342     * @hide 
40343     */
40344     /** 
40345     * @cfg {Number} growMax 
40346     * @hide 
40347     */
40348     /**
40349      * @hide
40350      * @method autoSize
40351      */
40352 });/*
40353  * Copyright(c) 2010-2012, Roo J Solutions Limited
40354  *
40355  * Licence LGPL
40356  *
40357  */
40358
40359 /**
40360  * @class Roo.form.ComboBoxArray
40361  * @extends Roo.form.TextField
40362  * A facebook style adder... for lists of email / people / countries  etc...
40363  * pick multiple items from a combo box, and shows each one.
40364  *
40365  *  Fred [x]  Brian [x]  [Pick another |v]
40366  *
40367  *
40368  *  For this to work: it needs various extra information
40369  *    - normal combo problay has
40370  *      name, hiddenName
40371  *    + displayField, valueField
40372  *
40373  *    For our purpose...
40374  *
40375  *
40376  *   If we change from 'extends' to wrapping...
40377  *   
40378  *  
40379  *
40380  
40381  
40382  * @constructor
40383  * Create a new ComboBoxArray.
40384  * @param {Object} config Configuration options
40385  */
40386  
40387
40388 Roo.form.ComboBoxArray = function(config)
40389 {
40390     this.addEvents({
40391         /**
40392          * @event beforeremove
40393          * Fires before remove the value from the list
40394              * @param {Roo.form.ComboBoxArray} _self This combo box array
40395              * @param {Roo.form.ComboBoxArray.Item} item removed item
40396              */
40397         'beforeremove' : true,
40398         /**
40399          * @event remove
40400          * Fires when remove the value from the list
40401              * @param {Roo.form.ComboBoxArray} _self This combo box array
40402              * @param {Roo.form.ComboBoxArray.Item} item removed item
40403              */
40404         'remove' : true
40405         
40406         
40407     });
40408     
40409     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40410     
40411     this.items = new Roo.util.MixedCollection(false);
40412     
40413     // construct the child combo...
40414     
40415     
40416     
40417     
40418    
40419     
40420 }
40421
40422  
40423 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40424
40425     /**
40426      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40427      */
40428     
40429     lastData : false,
40430     
40431     // behavies liek a hiddne field
40432     inputType:      'hidden',
40433     /**
40434      * @cfg {Number} width The width of the box that displays the selected element
40435      */ 
40436     width:          300,
40437
40438     
40439     
40440     /**
40441      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40442      */
40443     name : false,
40444     /**
40445      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40446      */
40447     hiddenName : false,
40448     
40449     
40450     // private the array of items that are displayed..
40451     items  : false,
40452     // private - the hidden field el.
40453     hiddenEl : false,
40454     // private - the filed el..
40455     el : false,
40456     
40457     //validateValue : function() { return true; }, // all values are ok!
40458     //onAddClick: function() { },
40459     
40460     onRender : function(ct, position) 
40461     {
40462         
40463         // create the standard hidden element
40464         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40465         
40466         
40467         // give fake names to child combo;
40468         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40469         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40470         
40471         this.combo = Roo.factory(this.combo, Roo.form);
40472         this.combo.onRender(ct, position);
40473         if (typeof(this.combo.width) != 'undefined') {
40474             this.combo.onResize(this.combo.width,0);
40475         }
40476         
40477         this.combo.initEvents();
40478         
40479         // assigned so form know we need to do this..
40480         this.store          = this.combo.store;
40481         this.valueField     = this.combo.valueField;
40482         this.displayField   = this.combo.displayField ;
40483         
40484         
40485         this.combo.wrap.addClass('x-cbarray-grp');
40486         
40487         var cbwrap = this.combo.wrap.createChild(
40488             {tag: 'div', cls: 'x-cbarray-cb'},
40489             this.combo.el.dom
40490         );
40491         
40492              
40493         this.hiddenEl = this.combo.wrap.createChild({
40494             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40495         });
40496         this.el = this.combo.wrap.createChild({
40497             tag: 'input',  type:'hidden' , name: this.name, value : ''
40498         });
40499          //   this.el.dom.removeAttribute("name");
40500         
40501         
40502         this.outerWrap = this.combo.wrap;
40503         this.wrap = cbwrap;
40504         
40505         this.outerWrap.setWidth(this.width);
40506         this.outerWrap.dom.removeChild(this.el.dom);
40507         
40508         this.wrap.dom.appendChild(this.el.dom);
40509         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40510         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40511         
40512         this.combo.trigger.setStyle('position','relative');
40513         this.combo.trigger.setStyle('left', '0px');
40514         this.combo.trigger.setStyle('top', '2px');
40515         
40516         this.combo.el.setStyle('vertical-align', 'text-bottom');
40517         
40518         //this.trigger.setStyle('vertical-align', 'top');
40519         
40520         // this should use the code from combo really... on('add' ....)
40521         if (this.adder) {
40522             
40523         
40524             this.adder = this.outerWrap.createChild(
40525                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40526             var _t = this;
40527             this.adder.on('click', function(e) {
40528                 _t.fireEvent('adderclick', this, e);
40529             }, _t);
40530         }
40531         //var _t = this;
40532         //this.adder.on('click', this.onAddClick, _t);
40533         
40534         
40535         this.combo.on('select', function(cb, rec, ix) {
40536             this.addItem(rec.data);
40537             
40538             cb.setValue('');
40539             cb.el.dom.value = '';
40540             //cb.lastData = rec.data;
40541             // add to list
40542             
40543         }, this);
40544         
40545         
40546     },
40547     
40548     
40549     getName: function()
40550     {
40551         // returns hidden if it's set..
40552         if (!this.rendered) {return ''};
40553         return  this.hiddenName ? this.hiddenName : this.name;
40554         
40555     },
40556     
40557     
40558     onResize: function(w, h){
40559         
40560         return;
40561         // not sure if this is needed..
40562         //this.combo.onResize(w,h);
40563         
40564         if(typeof w != 'number'){
40565             // we do not handle it!?!?
40566             return;
40567         }
40568         var tw = this.combo.trigger.getWidth();
40569         tw += this.addicon ? this.addicon.getWidth() : 0;
40570         tw += this.editicon ? this.editicon.getWidth() : 0;
40571         var x = w - tw;
40572         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40573             
40574         this.combo.trigger.setStyle('left', '0px');
40575         
40576         if(this.list && this.listWidth === undefined){
40577             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40578             this.list.setWidth(lw);
40579             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40580         }
40581         
40582     
40583         
40584     },
40585     
40586     addItem: function(rec)
40587     {
40588         var valueField = this.combo.valueField;
40589         var displayField = this.combo.displayField;
40590         if (this.items.indexOfKey(rec[valueField]) > -1) {
40591             //console.log("GOT " + rec.data.id);
40592             return;
40593         }
40594         
40595         var x = new Roo.form.ComboBoxArray.Item({
40596             //id : rec[this.idField],
40597             data : rec,
40598             displayField : displayField ,
40599             tipField : displayField ,
40600             cb : this
40601         });
40602         // use the 
40603         this.items.add(rec[valueField],x);
40604         // add it before the element..
40605         this.updateHiddenEl();
40606         x.render(this.outerWrap, this.wrap.dom);
40607         // add the image handler..
40608     },
40609     
40610     updateHiddenEl : function()
40611     {
40612         this.validate();
40613         if (!this.hiddenEl) {
40614             return;
40615         }
40616         var ar = [];
40617         var idField = this.combo.valueField;
40618         
40619         this.items.each(function(f) {
40620             ar.push(f.data[idField]);
40621            
40622         });
40623         this.hiddenEl.dom.value = ar.join(',');
40624         this.validate();
40625     },
40626     
40627     reset : function()
40628     {
40629         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40630         this.items.each(function(f) {
40631            f.remove(); 
40632         });
40633         this.el.dom.value = '';
40634         if (this.hiddenEl) {
40635             this.hiddenEl.dom.value = '';
40636         }
40637         
40638     },
40639     getValue: function()
40640     {
40641         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40642     },
40643     setValue: function(v) // not a valid action - must use addItems..
40644     {
40645          
40646         this.reset();
40647         
40648         
40649         
40650         if (this.store.isLocal && (typeof(v) == 'string')) {
40651             // then we can use the store to find the values..
40652             // comma seperated at present.. this needs to allow JSON based encoding..
40653             this.hiddenEl.value  = v;
40654             var v_ar = [];
40655             Roo.each(v.split(','), function(k) {
40656                 Roo.log("CHECK " + this.valueField + ',' + k);
40657                 var li = this.store.query(this.valueField, k);
40658                 if (!li.length) {
40659                     return;
40660                 }
40661                 var add = {};
40662                 add[this.valueField] = k;
40663                 add[this.displayField] = li.item(0).data[this.displayField];
40664                 
40665                 this.addItem(add);
40666             }, this) 
40667              
40668         }
40669         if (typeof(v) == 'object' ) {
40670             // then let's assume it's an array of objects..
40671             Roo.each(v, function(l) {
40672                 this.addItem(l);
40673             }, this);
40674              
40675         }
40676         
40677         
40678     },
40679     setFromData: function(v)
40680     {
40681         // this recieves an object, if setValues is called.
40682         this.reset();
40683         this.el.dom.value = v[this.displayField];
40684         this.hiddenEl.dom.value = v[this.valueField];
40685         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40686             return;
40687         }
40688         var kv = v[this.valueField];
40689         var dv = v[this.displayField];
40690         kv = typeof(kv) != 'string' ? '' : kv;
40691         dv = typeof(dv) != 'string' ? '' : dv;
40692         
40693         
40694         var keys = kv.split(',');
40695         var display = dv.split(',');
40696         for (var i = 0 ; i < keys.length; i++) {
40697             
40698             add = {};
40699             add[this.valueField] = keys[i];
40700             add[this.displayField] = display[i];
40701             this.addItem(add);
40702         }
40703       
40704         
40705     },
40706     
40707     /**
40708      * Validates the combox array value
40709      * @return {Boolean} True if the value is valid, else false
40710      */
40711     validate : function(){
40712         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40713             this.clearInvalid();
40714             return true;
40715         }
40716         return false;
40717     },
40718     
40719     validateValue : function(value){
40720         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40721         
40722     },
40723     
40724     /*@
40725      * overide
40726      * 
40727      */
40728     isDirty : function() {
40729         if(this.disabled) {
40730             return false;
40731         }
40732         
40733         try {
40734             var d = Roo.decode(String(this.originalValue));
40735         } catch (e) {
40736             return String(this.getValue()) !== String(this.originalValue);
40737         }
40738         
40739         var originalValue = [];
40740         
40741         for (var i = 0; i < d.length; i++){
40742             originalValue.push(d[i][this.valueField]);
40743         }
40744         
40745         return String(this.getValue()) !== String(originalValue.join(','));
40746         
40747     }
40748     
40749 });
40750
40751
40752
40753 /**
40754  * @class Roo.form.ComboBoxArray.Item
40755  * @extends Roo.BoxComponent
40756  * A selected item in the list
40757  *  Fred [x]  Brian [x]  [Pick another |v]
40758  * 
40759  * @constructor
40760  * Create a new item.
40761  * @param {Object} config Configuration options
40762  */
40763  
40764 Roo.form.ComboBoxArray.Item = function(config) {
40765     config.id = Roo.id();
40766     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40767 }
40768
40769 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40770     data : {},
40771     cb: false,
40772     displayField : false,
40773     tipField : false,
40774     
40775     
40776     defaultAutoCreate : {
40777         tag: 'div',
40778         cls: 'x-cbarray-item',
40779         cn : [ 
40780             { tag: 'div' },
40781             {
40782                 tag: 'img',
40783                 width:16,
40784                 height : 16,
40785                 src : Roo.BLANK_IMAGE_URL ,
40786                 align: 'center'
40787             }
40788         ]
40789         
40790     },
40791     
40792  
40793     onRender : function(ct, position)
40794     {
40795         Roo.form.Field.superclass.onRender.call(this, ct, position);
40796         
40797         if(!this.el){
40798             var cfg = this.getAutoCreate();
40799             this.el = ct.createChild(cfg, position);
40800         }
40801         
40802         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40803         
40804         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40805             this.cb.renderer(this.data) :
40806             String.format('{0}',this.data[this.displayField]);
40807         
40808             
40809         this.el.child('div').dom.setAttribute('qtip',
40810                         String.format('{0}',this.data[this.tipField])
40811         );
40812         
40813         this.el.child('img').on('click', this.remove, this);
40814         
40815     },
40816    
40817     remove : function()
40818     {
40819         if(this.cb.disabled){
40820             return;
40821         }
40822         
40823         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40824             this.cb.items.remove(this);
40825             this.el.child('img').un('click', this.remove, this);
40826             this.el.remove();
40827             this.cb.updateHiddenEl();
40828
40829             this.cb.fireEvent('remove', this.cb, this);
40830         }
40831         
40832     }
40833 });/*
40834  * Based on:
40835  * Ext JS Library 1.1.1
40836  * Copyright(c) 2006-2007, Ext JS, LLC.
40837  *
40838  * Originally Released Under LGPL - original licence link has changed is not relivant.
40839  *
40840  * Fork - LGPL
40841  * <script type="text/javascript">
40842  */
40843 /**
40844  * @class Roo.form.Checkbox
40845  * @extends Roo.form.Field
40846  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40847  * @constructor
40848  * Creates a new Checkbox
40849  * @param {Object} config Configuration options
40850  */
40851 Roo.form.Checkbox = function(config){
40852     Roo.form.Checkbox.superclass.constructor.call(this, config);
40853     this.addEvents({
40854         /**
40855          * @event check
40856          * Fires when the checkbox is checked or unchecked.
40857              * @param {Roo.form.Checkbox} this This checkbox
40858              * @param {Boolean} checked The new checked value
40859              */
40860         check : true
40861     });
40862 };
40863
40864 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40865     /**
40866      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40867      */
40868     focusClass : undefined,
40869     /**
40870      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40871      */
40872     fieldClass: "x-form-field",
40873     /**
40874      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40875      */
40876     checked: false,
40877     /**
40878      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40879      * {tag: "input", type: "checkbox", autocomplete: "off"})
40880      */
40881     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40882     /**
40883      * @cfg {String} boxLabel The text that appears beside the checkbox
40884      */
40885     boxLabel : "",
40886     /**
40887      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40888      */  
40889     inputValue : '1',
40890     /**
40891      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40892      */
40893      valueOff: '0', // value when not checked..
40894
40895     actionMode : 'viewEl', 
40896     //
40897     // private
40898     itemCls : 'x-menu-check-item x-form-item',
40899     groupClass : 'x-menu-group-item',
40900     inputType : 'hidden',
40901     
40902     
40903     inSetChecked: false, // check that we are not calling self...
40904     
40905     inputElement: false, // real input element?
40906     basedOn: false, // ????
40907     
40908     isFormField: true, // not sure where this is needed!!!!
40909
40910     onResize : function(){
40911         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40912         if(!this.boxLabel){
40913             this.el.alignTo(this.wrap, 'c-c');
40914         }
40915     },
40916
40917     initEvents : function(){
40918         Roo.form.Checkbox.superclass.initEvents.call(this);
40919         this.el.on("click", this.onClick,  this);
40920         this.el.on("change", this.onClick,  this);
40921     },
40922
40923
40924     getResizeEl : function(){
40925         return this.wrap;
40926     },
40927
40928     getPositionEl : function(){
40929         return this.wrap;
40930     },
40931
40932     // private
40933     onRender : function(ct, position){
40934         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40935         /*
40936         if(this.inputValue !== undefined){
40937             this.el.dom.value = this.inputValue;
40938         }
40939         */
40940         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40941         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40942         var viewEl = this.wrap.createChild({ 
40943             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40944         this.viewEl = viewEl;   
40945         this.wrap.on('click', this.onClick,  this); 
40946         
40947         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40948         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40949         
40950         
40951         
40952         if(this.boxLabel){
40953             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40954         //    viewEl.on('click', this.onClick,  this); 
40955         }
40956         //if(this.checked){
40957             this.setChecked(this.checked);
40958         //}else{
40959             //this.checked = this.el.dom;
40960         //}
40961
40962     },
40963
40964     // private
40965     initValue : Roo.emptyFn,
40966
40967     /**
40968      * Returns the checked state of the checkbox.
40969      * @return {Boolean} True if checked, else false
40970      */
40971     getValue : function(){
40972         if(this.el){
40973             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40974         }
40975         return this.valueOff;
40976         
40977     },
40978
40979         // private
40980     onClick : function(){ 
40981         if (this.disabled) {
40982             return;
40983         }
40984         this.setChecked(!this.checked);
40985
40986         //if(this.el.dom.checked != this.checked){
40987         //    this.setValue(this.el.dom.checked);
40988        // }
40989     },
40990
40991     /**
40992      * Sets the checked state of the checkbox.
40993      * On is always based on a string comparison between inputValue and the param.
40994      * @param {Boolean/String} value - the value to set 
40995      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40996      */
40997     setValue : function(v,suppressEvent){
40998         
40999         
41000         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
41001         //if(this.el && this.el.dom){
41002         //    this.el.dom.checked = this.checked;
41003         //    this.el.dom.defaultChecked = this.checked;
41004         //}
41005         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
41006         //this.fireEvent("check", this, this.checked);
41007     },
41008     // private..
41009     setChecked : function(state,suppressEvent)
41010     {
41011         if (this.inSetChecked) {
41012             this.checked = state;
41013             return;
41014         }
41015         
41016     
41017         if(this.wrap){
41018             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41019         }
41020         this.checked = state;
41021         if(suppressEvent !== true){
41022             this.fireEvent('check', this, state);
41023         }
41024         this.inSetChecked = true;
41025         this.el.dom.value = state ? this.inputValue : this.valueOff;
41026         this.inSetChecked = false;
41027         
41028     },
41029     // handle setting of hidden value by some other method!!?!?
41030     setFromHidden: function()
41031     {
41032         if(!this.el){
41033             return;
41034         }
41035         //console.log("SET FROM HIDDEN");
41036         //alert('setFrom hidden');
41037         this.setValue(this.el.dom.value);
41038     },
41039     
41040     onDestroy : function()
41041     {
41042         if(this.viewEl){
41043             Roo.get(this.viewEl).remove();
41044         }
41045          
41046         Roo.form.Checkbox.superclass.onDestroy.call(this);
41047     }
41048
41049 });/*
41050  * Based on:
41051  * Ext JS Library 1.1.1
41052  * Copyright(c) 2006-2007, Ext JS, LLC.
41053  *
41054  * Originally Released Under LGPL - original licence link has changed is not relivant.
41055  *
41056  * Fork - LGPL
41057  * <script type="text/javascript">
41058  */
41059  
41060 /**
41061  * @class Roo.form.Radio
41062  * @extends Roo.form.Checkbox
41063  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41064  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41065  * @constructor
41066  * Creates a new Radio
41067  * @param {Object} config Configuration options
41068  */
41069 Roo.form.Radio = function(){
41070     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41071 };
41072 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41073     inputType: 'radio',
41074
41075     /**
41076      * If this radio is part of a group, it will return the selected value
41077      * @return {String}
41078      */
41079     getGroupValue : function(){
41080         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41081     },
41082     
41083     
41084     onRender : function(ct, position){
41085         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41086         
41087         if(this.inputValue !== undefined){
41088             this.el.dom.value = this.inputValue;
41089         }
41090          
41091         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41092         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41093         //var viewEl = this.wrap.createChild({ 
41094         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41095         //this.viewEl = viewEl;   
41096         //this.wrap.on('click', this.onClick,  this); 
41097         
41098         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41099         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41100         
41101         
41102         
41103         if(this.boxLabel){
41104             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41105         //    viewEl.on('click', this.onClick,  this); 
41106         }
41107          if(this.checked){
41108             this.el.dom.checked =   'checked' ;
41109         }
41110          
41111     } 
41112     
41113     
41114 });//<script type="text/javascript">
41115
41116 /*
41117  * Based  Ext JS Library 1.1.1
41118  * Copyright(c) 2006-2007, Ext JS, LLC.
41119  * LGPL
41120  *
41121  */
41122  
41123 /**
41124  * @class Roo.HtmlEditorCore
41125  * @extends Roo.Component
41126  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41127  *
41128  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41129  */
41130
41131 Roo.HtmlEditorCore = function(config){
41132     
41133     
41134     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41135     
41136     
41137     this.addEvents({
41138         /**
41139          * @event initialize
41140          * Fires when the editor is fully initialized (including the iframe)
41141          * @param {Roo.HtmlEditorCore} this
41142          */
41143         initialize: true,
41144         /**
41145          * @event activate
41146          * Fires when the editor is first receives the focus. Any insertion must wait
41147          * until after this event.
41148          * @param {Roo.HtmlEditorCore} this
41149          */
41150         activate: true,
41151          /**
41152          * @event beforesync
41153          * Fires before the textarea is updated with content from the editor iframe. Return false
41154          * to cancel the sync.
41155          * @param {Roo.HtmlEditorCore} this
41156          * @param {String} html
41157          */
41158         beforesync: true,
41159          /**
41160          * @event beforepush
41161          * Fires before the iframe editor is updated with content from the textarea. Return false
41162          * to cancel the push.
41163          * @param {Roo.HtmlEditorCore} this
41164          * @param {String} html
41165          */
41166         beforepush: true,
41167          /**
41168          * @event sync
41169          * Fires when the textarea is updated with content from the editor iframe.
41170          * @param {Roo.HtmlEditorCore} this
41171          * @param {String} html
41172          */
41173         sync: true,
41174          /**
41175          * @event push
41176          * Fires when the iframe editor is updated with content from the textarea.
41177          * @param {Roo.HtmlEditorCore} this
41178          * @param {String} html
41179          */
41180         push: true,
41181         
41182         /**
41183          * @event editorevent
41184          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41185          * @param {Roo.HtmlEditorCore} this
41186          */
41187         editorevent: true
41188         
41189     });
41190     
41191     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41192     
41193     // defaults : white / black...
41194     this.applyBlacklists();
41195     
41196     
41197     
41198 };
41199
41200
41201 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41202
41203
41204      /**
41205      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41206      */
41207     
41208     owner : false,
41209     
41210      /**
41211      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41212      *                        Roo.resizable.
41213      */
41214     resizable : false,
41215      /**
41216      * @cfg {Number} height (in pixels)
41217      */   
41218     height: 300,
41219    /**
41220      * @cfg {Number} width (in pixels)
41221      */   
41222     width: 500,
41223     
41224     /**
41225      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41226      * 
41227      */
41228     stylesheets: false,
41229     
41230     // id of frame..
41231     frameId: false,
41232     
41233     // private properties
41234     validationEvent : false,
41235     deferHeight: true,
41236     initialized : false,
41237     activated : false,
41238     sourceEditMode : false,
41239     onFocus : Roo.emptyFn,
41240     iframePad:3,
41241     hideMode:'offsets',
41242     
41243     clearUp: true,
41244     
41245     // blacklist + whitelisted elements..
41246     black: false,
41247     white: false,
41248      
41249     
41250
41251     /**
41252      * Protected method that will not generally be called directly. It
41253      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41254      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41255      */
41256     getDocMarkup : function(){
41257         // body styles..
41258         var st = '';
41259         
41260         // inherit styels from page...?? 
41261         if (this.stylesheets === false) {
41262             
41263             Roo.get(document.head).select('style').each(function(node) {
41264                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41265             });
41266             
41267             Roo.get(document.head).select('link').each(function(node) { 
41268                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41269             });
41270             
41271         } else if (!this.stylesheets.length) {
41272                 // simple..
41273                 st = '<style type="text/css">' +
41274                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41275                    '</style>';
41276         } else { 
41277             
41278         }
41279         
41280         st +=  '<style type="text/css">' +
41281             'IMG { cursor: pointer } ' +
41282         '</style>';
41283
41284         
41285         return '<html><head>' + st  +
41286             //<style type="text/css">' +
41287             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41288             //'</style>' +
41289             ' </head><body class="roo-htmleditor-body"></body></html>';
41290     },
41291
41292     // private
41293     onRender : function(ct, position)
41294     {
41295         var _t = this;
41296         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41297         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41298         
41299         
41300         this.el.dom.style.border = '0 none';
41301         this.el.dom.setAttribute('tabIndex', -1);
41302         this.el.addClass('x-hidden hide');
41303         
41304         
41305         
41306         if(Roo.isIE){ // fix IE 1px bogus margin
41307             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41308         }
41309        
41310         
41311         this.frameId = Roo.id();
41312         
41313          
41314         
41315         var iframe = this.owner.wrap.createChild({
41316             tag: 'iframe',
41317             cls: 'form-control', // bootstrap..
41318             id: this.frameId,
41319             name: this.frameId,
41320             frameBorder : 'no',
41321             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41322         }, this.el
41323         );
41324         
41325         
41326         this.iframe = iframe.dom;
41327
41328          this.assignDocWin();
41329         
41330         this.doc.designMode = 'on';
41331        
41332         this.doc.open();
41333         this.doc.write(this.getDocMarkup());
41334         this.doc.close();
41335
41336         
41337         var task = { // must defer to wait for browser to be ready
41338             run : function(){
41339                 //console.log("run task?" + this.doc.readyState);
41340                 this.assignDocWin();
41341                 if(this.doc.body || this.doc.readyState == 'complete'){
41342                     try {
41343                         this.doc.designMode="on";
41344                     } catch (e) {
41345                         return;
41346                     }
41347                     Roo.TaskMgr.stop(task);
41348                     this.initEditor.defer(10, this);
41349                 }
41350             },
41351             interval : 10,
41352             duration: 10000,
41353             scope: this
41354         };
41355         Roo.TaskMgr.start(task);
41356
41357     },
41358
41359     // private
41360     onResize : function(w, h)
41361     {
41362          Roo.log('resize: ' +w + ',' + h );
41363         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41364         if(!this.iframe){
41365             return;
41366         }
41367         if(typeof w == 'number'){
41368             
41369             this.iframe.style.width = w + 'px';
41370         }
41371         if(typeof h == 'number'){
41372             
41373             this.iframe.style.height = h + 'px';
41374             if(this.doc){
41375                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41376             }
41377         }
41378         
41379     },
41380
41381     /**
41382      * Toggles the editor between standard and source edit mode.
41383      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41384      */
41385     toggleSourceEdit : function(sourceEditMode){
41386         
41387         this.sourceEditMode = sourceEditMode === true;
41388         
41389         if(this.sourceEditMode){
41390  
41391             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41392             
41393         }else{
41394             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41395             //this.iframe.className = '';
41396             this.deferFocus();
41397         }
41398         //this.setSize(this.owner.wrap.getSize());
41399         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41400     },
41401
41402     
41403   
41404
41405     /**
41406      * Protected method that will not generally be called directly. If you need/want
41407      * custom HTML cleanup, this is the method you should override.
41408      * @param {String} html The HTML to be cleaned
41409      * return {String} The cleaned HTML
41410      */
41411     cleanHtml : function(html){
41412         html = String(html);
41413         if(html.length > 5){
41414             if(Roo.isSafari){ // strip safari nonsense
41415                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41416             }
41417         }
41418         if(html == '&nbsp;'){
41419             html = '';
41420         }
41421         return html;
41422     },
41423
41424     /**
41425      * HTML Editor -> Textarea
41426      * Protected method that will not generally be called directly. Syncs the contents
41427      * of the editor iframe with the textarea.
41428      */
41429     syncValue : function(){
41430         if(this.initialized){
41431             var bd = (this.doc.body || this.doc.documentElement);
41432             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41433             var html = bd.innerHTML;
41434             if(Roo.isSafari){
41435                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41436                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41437                 if(m && m[1]){
41438                     html = '<div style="'+m[0]+'">' + html + '</div>';
41439                 }
41440             }
41441             html = this.cleanHtml(html);
41442             // fix up the special chars.. normaly like back quotes in word...
41443             // however we do not want to do this with chinese..
41444             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41445                 var cc = b.charCodeAt();
41446                 if (
41447                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41448                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41449                     (cc >= 0xf900 && cc < 0xfb00 )
41450                 ) {
41451                         return b;
41452                 }
41453                 return "&#"+cc+";" 
41454             });
41455             if(this.owner.fireEvent('beforesync', this, html) !== false){
41456                 this.el.dom.value = html;
41457                 this.owner.fireEvent('sync', this, html);
41458             }
41459         }
41460     },
41461
41462     /**
41463      * Protected method that will not generally be called directly. Pushes the value of the textarea
41464      * into the iframe editor.
41465      */
41466     pushValue : function(){
41467         if(this.initialized){
41468             var v = this.el.dom.value.trim();
41469             
41470 //            if(v.length < 1){
41471 //                v = '&#160;';
41472 //            }
41473             
41474             if(this.owner.fireEvent('beforepush', this, v) !== false){
41475                 var d = (this.doc.body || this.doc.documentElement);
41476                 d.innerHTML = v;
41477                 this.cleanUpPaste();
41478                 this.el.dom.value = d.innerHTML;
41479                 this.owner.fireEvent('push', this, v);
41480             }
41481         }
41482     },
41483
41484     // private
41485     deferFocus : function(){
41486         this.focus.defer(10, this);
41487     },
41488
41489     // doc'ed in Field
41490     focus : function(){
41491         if(this.win && !this.sourceEditMode){
41492             this.win.focus();
41493         }else{
41494             this.el.focus();
41495         }
41496     },
41497     
41498     assignDocWin: function()
41499     {
41500         var iframe = this.iframe;
41501         
41502          if(Roo.isIE){
41503             this.doc = iframe.contentWindow.document;
41504             this.win = iframe.contentWindow;
41505         } else {
41506 //            if (!Roo.get(this.frameId)) {
41507 //                return;
41508 //            }
41509 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41510 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41511             
41512             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41513                 return;
41514             }
41515             
41516             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41517             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41518         }
41519     },
41520     
41521     // private
41522     initEditor : function(){
41523         //console.log("INIT EDITOR");
41524         this.assignDocWin();
41525         
41526         
41527         
41528         this.doc.designMode="on";
41529         this.doc.open();
41530         this.doc.write(this.getDocMarkup());
41531         this.doc.close();
41532         
41533         var dbody = (this.doc.body || this.doc.documentElement);
41534         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41535         // this copies styles from the containing element into thsi one..
41536         // not sure why we need all of this..
41537         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41538         
41539         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41540         //ss['background-attachment'] = 'fixed'; // w3c
41541         dbody.bgProperties = 'fixed'; // ie
41542         //Roo.DomHelper.applyStyles(dbody, ss);
41543         Roo.EventManager.on(this.doc, {
41544             //'mousedown': this.onEditorEvent,
41545             'mouseup': this.onEditorEvent,
41546             'dblclick': this.onEditorEvent,
41547             'click': this.onEditorEvent,
41548             'keyup': this.onEditorEvent,
41549             buffer:100,
41550             scope: this
41551         });
41552         if(Roo.isGecko){
41553             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41554         }
41555         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41556             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41557         }
41558         this.initialized = true;
41559
41560         this.owner.fireEvent('initialize', this);
41561         this.pushValue();
41562     },
41563
41564     // private
41565     onDestroy : function(){
41566         
41567         
41568         
41569         if(this.rendered){
41570             
41571             //for (var i =0; i < this.toolbars.length;i++) {
41572             //    // fixme - ask toolbars for heights?
41573             //    this.toolbars[i].onDestroy();
41574            // }
41575             
41576             //this.wrap.dom.innerHTML = '';
41577             //this.wrap.remove();
41578         }
41579     },
41580
41581     // private
41582     onFirstFocus : function(){
41583         
41584         this.assignDocWin();
41585         
41586         
41587         this.activated = true;
41588          
41589     
41590         if(Roo.isGecko){ // prevent silly gecko errors
41591             this.win.focus();
41592             var s = this.win.getSelection();
41593             if(!s.focusNode || s.focusNode.nodeType != 3){
41594                 var r = s.getRangeAt(0);
41595                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41596                 r.collapse(true);
41597                 this.deferFocus();
41598             }
41599             try{
41600                 this.execCmd('useCSS', true);
41601                 this.execCmd('styleWithCSS', false);
41602             }catch(e){}
41603         }
41604         this.owner.fireEvent('activate', this);
41605     },
41606
41607     // private
41608     adjustFont: function(btn){
41609         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41610         //if(Roo.isSafari){ // safari
41611         //    adjust *= 2;
41612        // }
41613         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41614         if(Roo.isSafari){ // safari
41615             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41616             v =  (v < 10) ? 10 : v;
41617             v =  (v > 48) ? 48 : v;
41618             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41619             
41620         }
41621         
41622         
41623         v = Math.max(1, v+adjust);
41624         
41625         this.execCmd('FontSize', v  );
41626     },
41627
41628     onEditorEvent : function(e)
41629     {
41630         this.owner.fireEvent('editorevent', this, e);
41631       //  this.updateToolbar();
41632         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41633     },
41634
41635     insertTag : function(tg)
41636     {
41637         // could be a bit smarter... -> wrap the current selected tRoo..
41638         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41639             
41640             range = this.createRange(this.getSelection());
41641             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41642             wrappingNode.appendChild(range.extractContents());
41643             range.insertNode(wrappingNode);
41644
41645             return;
41646             
41647             
41648             
41649         }
41650         this.execCmd("formatblock",   tg);
41651         
41652     },
41653     
41654     insertText : function(txt)
41655     {
41656         
41657         
41658         var range = this.createRange();
41659         range.deleteContents();
41660                //alert(Sender.getAttribute('label'));
41661                
41662         range.insertNode(this.doc.createTextNode(txt));
41663     } ,
41664     
41665      
41666
41667     /**
41668      * Executes a Midas editor command on the editor document and performs necessary focus and
41669      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41670      * @param {String} cmd The Midas command
41671      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41672      */
41673     relayCmd : function(cmd, value){
41674         this.win.focus();
41675         this.execCmd(cmd, value);
41676         this.owner.fireEvent('editorevent', this);
41677         //this.updateToolbar();
41678         this.owner.deferFocus();
41679     },
41680
41681     /**
41682      * Executes a Midas editor command directly on the editor document.
41683      * For visual commands, you should use {@link #relayCmd} instead.
41684      * <b>This should only be called after the editor is initialized.</b>
41685      * @param {String} cmd The Midas command
41686      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41687      */
41688     execCmd : function(cmd, value){
41689         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41690         this.syncValue();
41691     },
41692  
41693  
41694    
41695     /**
41696      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41697      * to insert tRoo.
41698      * @param {String} text | dom node.. 
41699      */
41700     insertAtCursor : function(text)
41701     {
41702         
41703         
41704         
41705         if(!this.activated){
41706             return;
41707         }
41708         /*
41709         if(Roo.isIE){
41710             this.win.focus();
41711             var r = this.doc.selection.createRange();
41712             if(r){
41713                 r.collapse(true);
41714                 r.pasteHTML(text);
41715                 this.syncValue();
41716                 this.deferFocus();
41717             
41718             }
41719             return;
41720         }
41721         */
41722         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41723             this.win.focus();
41724             
41725             
41726             // from jquery ui (MIT licenced)
41727             var range, node;
41728             var win = this.win;
41729             
41730             if (win.getSelection && win.getSelection().getRangeAt) {
41731                 range = win.getSelection().getRangeAt(0);
41732                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41733                 range.insertNode(node);
41734             } else if (win.document.selection && win.document.selection.createRange) {
41735                 // no firefox support
41736                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41737                 win.document.selection.createRange().pasteHTML(txt);
41738             } else {
41739                 // no firefox support
41740                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41741                 this.execCmd('InsertHTML', txt);
41742             } 
41743             
41744             this.syncValue();
41745             
41746             this.deferFocus();
41747         }
41748     },
41749  // private
41750     mozKeyPress : function(e){
41751         if(e.ctrlKey){
41752             var c = e.getCharCode(), cmd;
41753           
41754             if(c > 0){
41755                 c = String.fromCharCode(c).toLowerCase();
41756                 switch(c){
41757                     case 'b':
41758                         cmd = 'bold';
41759                         break;
41760                     case 'i':
41761                         cmd = 'italic';
41762                         break;
41763                     
41764                     case 'u':
41765                         cmd = 'underline';
41766                         break;
41767                     
41768                     case 'v':
41769                         this.cleanUpPaste.defer(100, this);
41770                         return;
41771                         
41772                 }
41773                 if(cmd){
41774                     this.win.focus();
41775                     this.execCmd(cmd);
41776                     this.deferFocus();
41777                     e.preventDefault();
41778                 }
41779                 
41780             }
41781         }
41782     },
41783
41784     // private
41785     fixKeys : function(){ // load time branching for fastest keydown performance
41786         if(Roo.isIE){
41787             return function(e){
41788                 var k = e.getKey(), r;
41789                 if(k == e.TAB){
41790                     e.stopEvent();
41791                     r = this.doc.selection.createRange();
41792                     if(r){
41793                         r.collapse(true);
41794                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41795                         this.deferFocus();
41796                     }
41797                     return;
41798                 }
41799                 
41800                 if(k == e.ENTER){
41801                     r = this.doc.selection.createRange();
41802                     if(r){
41803                         var target = r.parentElement();
41804                         if(!target || target.tagName.toLowerCase() != 'li'){
41805                             e.stopEvent();
41806                             r.pasteHTML('<br />');
41807                             r.collapse(false);
41808                             r.select();
41809                         }
41810                     }
41811                 }
41812                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41813                     this.cleanUpPaste.defer(100, this);
41814                     return;
41815                 }
41816                 
41817                 
41818             };
41819         }else if(Roo.isOpera){
41820             return function(e){
41821                 var k = e.getKey();
41822                 if(k == e.TAB){
41823                     e.stopEvent();
41824                     this.win.focus();
41825                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41826                     this.deferFocus();
41827                 }
41828                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41829                     this.cleanUpPaste.defer(100, this);
41830                     return;
41831                 }
41832                 
41833             };
41834         }else if(Roo.isSafari){
41835             return function(e){
41836                 var k = e.getKey();
41837                 
41838                 if(k == e.TAB){
41839                     e.stopEvent();
41840                     this.execCmd('InsertText','\t');
41841                     this.deferFocus();
41842                     return;
41843                 }
41844                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41845                     this.cleanUpPaste.defer(100, this);
41846                     return;
41847                 }
41848                 
41849              };
41850         }
41851     }(),
41852     
41853     getAllAncestors: function()
41854     {
41855         var p = this.getSelectedNode();
41856         var a = [];
41857         if (!p) {
41858             a.push(p); // push blank onto stack..
41859             p = this.getParentElement();
41860         }
41861         
41862         
41863         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41864             a.push(p);
41865             p = p.parentNode;
41866         }
41867         a.push(this.doc.body);
41868         return a;
41869     },
41870     lastSel : false,
41871     lastSelNode : false,
41872     
41873     
41874     getSelection : function() 
41875     {
41876         this.assignDocWin();
41877         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41878     },
41879     
41880     getSelectedNode: function() 
41881     {
41882         // this may only work on Gecko!!!
41883         
41884         // should we cache this!!!!
41885         
41886         
41887         
41888          
41889         var range = this.createRange(this.getSelection()).cloneRange();
41890         
41891         if (Roo.isIE) {
41892             var parent = range.parentElement();
41893             while (true) {
41894                 var testRange = range.duplicate();
41895                 testRange.moveToElementText(parent);
41896                 if (testRange.inRange(range)) {
41897                     break;
41898                 }
41899                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41900                     break;
41901                 }
41902                 parent = parent.parentElement;
41903             }
41904             return parent;
41905         }
41906         
41907         // is ancestor a text element.
41908         var ac =  range.commonAncestorContainer;
41909         if (ac.nodeType == 3) {
41910             ac = ac.parentNode;
41911         }
41912         
41913         var ar = ac.childNodes;
41914          
41915         var nodes = [];
41916         var other_nodes = [];
41917         var has_other_nodes = false;
41918         for (var i=0;i<ar.length;i++) {
41919             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41920                 continue;
41921             }
41922             // fullly contained node.
41923             
41924             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41925                 nodes.push(ar[i]);
41926                 continue;
41927             }
41928             
41929             // probably selected..
41930             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41931                 other_nodes.push(ar[i]);
41932                 continue;
41933             }
41934             // outer..
41935             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41936                 continue;
41937             }
41938             
41939             
41940             has_other_nodes = true;
41941         }
41942         if (!nodes.length && other_nodes.length) {
41943             nodes= other_nodes;
41944         }
41945         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41946             return false;
41947         }
41948         
41949         return nodes[0];
41950     },
41951     createRange: function(sel)
41952     {
41953         // this has strange effects when using with 
41954         // top toolbar - not sure if it's a great idea.
41955         //this.editor.contentWindow.focus();
41956         if (typeof sel != "undefined") {
41957             try {
41958                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41959             } catch(e) {
41960                 return this.doc.createRange();
41961             }
41962         } else {
41963             return this.doc.createRange();
41964         }
41965     },
41966     getParentElement: function()
41967     {
41968         
41969         this.assignDocWin();
41970         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41971         
41972         var range = this.createRange(sel);
41973          
41974         try {
41975             var p = range.commonAncestorContainer;
41976             while (p.nodeType == 3) { // text node
41977                 p = p.parentNode;
41978             }
41979             return p;
41980         } catch (e) {
41981             return null;
41982         }
41983     
41984     },
41985     /***
41986      *
41987      * Range intersection.. the hard stuff...
41988      *  '-1' = before
41989      *  '0' = hits..
41990      *  '1' = after.
41991      *         [ -- selected range --- ]
41992      *   [fail]                        [fail]
41993      *
41994      *    basically..
41995      *      if end is before start or  hits it. fail.
41996      *      if start is after end or hits it fail.
41997      *
41998      *   if either hits (but other is outside. - then it's not 
41999      *   
42000      *    
42001      **/
42002     
42003     
42004     // @see http://www.thismuchiknow.co.uk/?p=64.
42005     rangeIntersectsNode : function(range, node)
42006     {
42007         var nodeRange = node.ownerDocument.createRange();
42008         try {
42009             nodeRange.selectNode(node);
42010         } catch (e) {
42011             nodeRange.selectNodeContents(node);
42012         }
42013     
42014         var rangeStartRange = range.cloneRange();
42015         rangeStartRange.collapse(true);
42016     
42017         var rangeEndRange = range.cloneRange();
42018         rangeEndRange.collapse(false);
42019     
42020         var nodeStartRange = nodeRange.cloneRange();
42021         nodeStartRange.collapse(true);
42022     
42023         var nodeEndRange = nodeRange.cloneRange();
42024         nodeEndRange.collapse(false);
42025     
42026         return rangeStartRange.compareBoundaryPoints(
42027                  Range.START_TO_START, nodeEndRange) == -1 &&
42028                rangeEndRange.compareBoundaryPoints(
42029                  Range.START_TO_START, nodeStartRange) == 1;
42030         
42031          
42032     },
42033     rangeCompareNode : function(range, node)
42034     {
42035         var nodeRange = node.ownerDocument.createRange();
42036         try {
42037             nodeRange.selectNode(node);
42038         } catch (e) {
42039             nodeRange.selectNodeContents(node);
42040         }
42041         
42042         
42043         range.collapse(true);
42044     
42045         nodeRange.collapse(true);
42046      
42047         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42048         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42049          
42050         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42051         
42052         var nodeIsBefore   =  ss == 1;
42053         var nodeIsAfter    = ee == -1;
42054         
42055         if (nodeIsBefore && nodeIsAfter)
42056             return 0; // outer
42057         if (!nodeIsBefore && nodeIsAfter)
42058             return 1; //right trailed.
42059         
42060         if (nodeIsBefore && !nodeIsAfter)
42061             return 2;  // left trailed.
42062         // fully contined.
42063         return 3;
42064     },
42065
42066     // private? - in a new class?
42067     cleanUpPaste :  function()
42068     {
42069         // cleans up the whole document..
42070         Roo.log('cleanuppaste');
42071         
42072         this.cleanUpChildren(this.doc.body);
42073         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42074         if (clean != this.doc.body.innerHTML) {
42075             this.doc.body.innerHTML = clean;
42076         }
42077         
42078     },
42079     
42080     cleanWordChars : function(input) {// change the chars to hex code
42081         var he = Roo.HtmlEditorCore;
42082         
42083         var output = input;
42084         Roo.each(he.swapCodes, function(sw) { 
42085             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42086             
42087             output = output.replace(swapper, sw[1]);
42088         });
42089         
42090         return output;
42091     },
42092     
42093     
42094     cleanUpChildren : function (n)
42095     {
42096         if (!n.childNodes.length) {
42097             return;
42098         }
42099         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42100            this.cleanUpChild(n.childNodes[i]);
42101         }
42102     },
42103     
42104     
42105         
42106     
42107     cleanUpChild : function (node)
42108     {
42109         var ed = this;
42110         //console.log(node);
42111         if (node.nodeName == "#text") {
42112             // clean up silly Windows -- stuff?
42113             return; 
42114         }
42115         if (node.nodeName == "#comment") {
42116             node.parentNode.removeChild(node);
42117             // clean up silly Windows -- stuff?
42118             return; 
42119         }
42120         var lcname = node.tagName.toLowerCase();
42121         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42122         // whitelist of tags..
42123         
42124         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42125             // remove node.
42126             node.parentNode.removeChild(node);
42127             return;
42128             
42129         }
42130         
42131         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42132         
42133         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42134         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42135         
42136         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42137         //    remove_keep_children = true;
42138         //}
42139         
42140         if (remove_keep_children) {
42141             this.cleanUpChildren(node);
42142             // inserts everything just before this node...
42143             while (node.childNodes.length) {
42144                 var cn = node.childNodes[0];
42145                 node.removeChild(cn);
42146                 node.parentNode.insertBefore(cn, node);
42147             }
42148             node.parentNode.removeChild(node);
42149             return;
42150         }
42151         
42152         if (!node.attributes || !node.attributes.length) {
42153             this.cleanUpChildren(node);
42154             return;
42155         }
42156         
42157         function cleanAttr(n,v)
42158         {
42159             
42160             if (v.match(/^\./) || v.match(/^\//)) {
42161                 return;
42162             }
42163             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42164                 return;
42165             }
42166             if (v.match(/^#/)) {
42167                 return;
42168             }
42169 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42170             node.removeAttribute(n);
42171             
42172         }
42173         
42174         var cwhite = this.cwhite;
42175         var cblack = this.cblack;
42176             
42177         function cleanStyle(n,v)
42178         {
42179             if (v.match(/expression/)) { //XSS?? should we even bother..
42180                 node.removeAttribute(n);
42181                 return;
42182             }
42183             
42184             var parts = v.split(/;/);
42185             var clean = [];
42186             
42187             Roo.each(parts, function(p) {
42188                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42189                 if (!p.length) {
42190                     return true;
42191                 }
42192                 var l = p.split(':').shift().replace(/\s+/g,'');
42193                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42194                 
42195                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42196 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42197                     //node.removeAttribute(n);
42198                     return true;
42199                 }
42200                 //Roo.log()
42201                 // only allow 'c whitelisted system attributes'
42202                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42203 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42204                     //node.removeAttribute(n);
42205                     return true;
42206                 }
42207                 
42208                 
42209                  
42210                 
42211                 clean.push(p);
42212                 return true;
42213             });
42214             if (clean.length) { 
42215                 node.setAttribute(n, clean.join(';'));
42216             } else {
42217                 node.removeAttribute(n);
42218             }
42219             
42220         }
42221         
42222         
42223         for (var i = node.attributes.length-1; i > -1 ; i--) {
42224             var a = node.attributes[i];
42225             //console.log(a);
42226             
42227             if (a.name.toLowerCase().substr(0,2)=='on')  {
42228                 node.removeAttribute(a.name);
42229                 continue;
42230             }
42231             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42232                 node.removeAttribute(a.name);
42233                 continue;
42234             }
42235             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42236                 cleanAttr(a.name,a.value); // fixme..
42237                 continue;
42238             }
42239             if (a.name == 'style') {
42240                 cleanStyle(a.name,a.value);
42241                 continue;
42242             }
42243             /// clean up MS crap..
42244             // tecnically this should be a list of valid class'es..
42245             
42246             
42247             if (a.name == 'class') {
42248                 if (a.value.match(/^Mso/)) {
42249                     node.className = '';
42250                 }
42251                 
42252                 if (a.value.match(/body/)) {
42253                     node.className = '';
42254                 }
42255                 continue;
42256             }
42257             
42258             // style cleanup!?
42259             // class cleanup?
42260             
42261         }
42262         
42263         
42264         this.cleanUpChildren(node);
42265         
42266         
42267     },
42268     
42269     /**
42270      * Clean up MS wordisms...
42271      */
42272     cleanWord : function(node)
42273     {
42274         
42275         
42276         if (!node) {
42277             this.cleanWord(this.doc.body);
42278             return;
42279         }
42280         if (node.nodeName == "#text") {
42281             // clean up silly Windows -- stuff?
42282             return; 
42283         }
42284         if (node.nodeName == "#comment") {
42285             node.parentNode.removeChild(node);
42286             // clean up silly Windows -- stuff?
42287             return; 
42288         }
42289         
42290         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42291             node.parentNode.removeChild(node);
42292             return;
42293         }
42294         
42295         // remove - but keep children..
42296         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42297             while (node.childNodes.length) {
42298                 var cn = node.childNodes[0];
42299                 node.removeChild(cn);
42300                 node.parentNode.insertBefore(cn, node);
42301             }
42302             node.parentNode.removeChild(node);
42303             this.iterateChildren(node, this.cleanWord);
42304             return;
42305         }
42306         // clean styles
42307         if (node.className.length) {
42308             
42309             var cn = node.className.split(/\W+/);
42310             var cna = [];
42311             Roo.each(cn, function(cls) {
42312                 if (cls.match(/Mso[a-zA-Z]+/)) {
42313                     return;
42314                 }
42315                 cna.push(cls);
42316             });
42317             node.className = cna.length ? cna.join(' ') : '';
42318             if (!cna.length) {
42319                 node.removeAttribute("class");
42320             }
42321         }
42322         
42323         if (node.hasAttribute("lang")) {
42324             node.removeAttribute("lang");
42325         }
42326         
42327         if (node.hasAttribute("style")) {
42328             
42329             var styles = node.getAttribute("style").split(";");
42330             var nstyle = [];
42331             Roo.each(styles, function(s) {
42332                 if (!s.match(/:/)) {
42333                     return;
42334                 }
42335                 var kv = s.split(":");
42336                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42337                     return;
42338                 }
42339                 // what ever is left... we allow.
42340                 nstyle.push(s);
42341             });
42342             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42343             if (!nstyle.length) {
42344                 node.removeAttribute('style');
42345             }
42346         }
42347         this.iterateChildren(node, this.cleanWord);
42348         
42349         
42350         
42351     },
42352     /**
42353      * iterateChildren of a Node, calling fn each time, using this as the scole..
42354      * @param {DomNode} node node to iterate children of.
42355      * @param {Function} fn method of this class to call on each item.
42356      */
42357     iterateChildren : function(node, fn)
42358     {
42359         if (!node.childNodes.length) {
42360                 return;
42361         }
42362         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42363            fn.call(this, node.childNodes[i])
42364         }
42365     },
42366     
42367     
42368     /**
42369      * cleanTableWidths.
42370      *
42371      * Quite often pasting from word etc.. results in tables with column and widths.
42372      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42373      *
42374      */
42375     cleanTableWidths : function(node)
42376     {
42377          
42378          
42379         if (!node) {
42380             this.cleanTableWidths(this.doc.body);
42381             return;
42382         }
42383         
42384         // ignore list...
42385         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42386             return; 
42387         }
42388         Roo.log(node.tagName);
42389         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42390             this.iterateChildren(node, this.cleanTableWidths);
42391             return;
42392         }
42393         if (node.hasAttribute('width')) {
42394             node.removeAttribute('width');
42395         }
42396         
42397          
42398         if (node.hasAttribute("style")) {
42399             // pretty basic...
42400             
42401             var styles = node.getAttribute("style").split(";");
42402             var nstyle = [];
42403             Roo.each(styles, function(s) {
42404                 if (!s.match(/:/)) {
42405                     return;
42406                 }
42407                 var kv = s.split(":");
42408                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42409                     return;
42410                 }
42411                 // what ever is left... we allow.
42412                 nstyle.push(s);
42413             });
42414             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42415             if (!nstyle.length) {
42416                 node.removeAttribute('style');
42417             }
42418         }
42419         
42420         this.iterateChildren(node, this.cleanTableWidths);
42421         
42422         
42423     },
42424     
42425     
42426     
42427     
42428     domToHTML : function(currentElement, depth, nopadtext) {
42429         
42430         depth = depth || 0;
42431         nopadtext = nopadtext || false;
42432     
42433         if (!currentElement) {
42434             return this.domToHTML(this.doc.body);
42435         }
42436         
42437         //Roo.log(currentElement);
42438         var j;
42439         var allText = false;
42440         var nodeName = currentElement.nodeName;
42441         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42442         
42443         if  (nodeName == '#text') {
42444             
42445             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42446         }
42447         
42448         
42449         var ret = '';
42450         if (nodeName != 'BODY') {
42451              
42452             var i = 0;
42453             // Prints the node tagName, such as <A>, <IMG>, etc
42454             if (tagName) {
42455                 var attr = [];
42456                 for(i = 0; i < currentElement.attributes.length;i++) {
42457                     // quoting?
42458                     var aname = currentElement.attributes.item(i).name;
42459                     if (!currentElement.attributes.item(i).value.length) {
42460                         continue;
42461                     }
42462                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42463                 }
42464                 
42465                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42466             } 
42467             else {
42468                 
42469                 // eack
42470             }
42471         } else {
42472             tagName = false;
42473         }
42474         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42475             return ret;
42476         }
42477         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42478             nopadtext = true;
42479         }
42480         
42481         
42482         // Traverse the tree
42483         i = 0;
42484         var currentElementChild = currentElement.childNodes.item(i);
42485         var allText = true;
42486         var innerHTML  = '';
42487         lastnode = '';
42488         while (currentElementChild) {
42489             // Formatting code (indent the tree so it looks nice on the screen)
42490             var nopad = nopadtext;
42491             if (lastnode == 'SPAN') {
42492                 nopad  = true;
42493             }
42494             // text
42495             if  (currentElementChild.nodeName == '#text') {
42496                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42497                 toadd = nopadtext ? toadd : toadd.trim();
42498                 if (!nopad && toadd.length > 80) {
42499                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42500                 }
42501                 innerHTML  += toadd;
42502                 
42503                 i++;
42504                 currentElementChild = currentElement.childNodes.item(i);
42505                 lastNode = '';
42506                 continue;
42507             }
42508             allText = false;
42509             
42510             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42511                 
42512             // Recursively traverse the tree structure of the child node
42513             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42514             lastnode = currentElementChild.nodeName;
42515             i++;
42516             currentElementChild=currentElement.childNodes.item(i);
42517         }
42518         
42519         ret += innerHTML;
42520         
42521         if (!allText) {
42522                 // The remaining code is mostly for formatting the tree
42523             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42524         }
42525         
42526         
42527         if (tagName) {
42528             ret+= "</"+tagName+">";
42529         }
42530         return ret;
42531         
42532     },
42533         
42534     applyBlacklists : function()
42535     {
42536         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42537         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42538         
42539         this.white = [];
42540         this.black = [];
42541         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42542             if (b.indexOf(tag) > -1) {
42543                 return;
42544             }
42545             this.white.push(tag);
42546             
42547         }, this);
42548         
42549         Roo.each(w, function(tag) {
42550             if (b.indexOf(tag) > -1) {
42551                 return;
42552             }
42553             if (this.white.indexOf(tag) > -1) {
42554                 return;
42555             }
42556             this.white.push(tag);
42557             
42558         }, this);
42559         
42560         
42561         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42562             if (w.indexOf(tag) > -1) {
42563                 return;
42564             }
42565             this.black.push(tag);
42566             
42567         }, this);
42568         
42569         Roo.each(b, function(tag) {
42570             if (w.indexOf(tag) > -1) {
42571                 return;
42572             }
42573             if (this.black.indexOf(tag) > -1) {
42574                 return;
42575             }
42576             this.black.push(tag);
42577             
42578         }, this);
42579         
42580         
42581         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42582         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42583         
42584         this.cwhite = [];
42585         this.cblack = [];
42586         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42587             if (b.indexOf(tag) > -1) {
42588                 return;
42589             }
42590             this.cwhite.push(tag);
42591             
42592         }, this);
42593         
42594         Roo.each(w, function(tag) {
42595             if (b.indexOf(tag) > -1) {
42596                 return;
42597             }
42598             if (this.cwhite.indexOf(tag) > -1) {
42599                 return;
42600             }
42601             this.cwhite.push(tag);
42602             
42603         }, this);
42604         
42605         
42606         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42607             if (w.indexOf(tag) > -1) {
42608                 return;
42609             }
42610             this.cblack.push(tag);
42611             
42612         }, this);
42613         
42614         Roo.each(b, function(tag) {
42615             if (w.indexOf(tag) > -1) {
42616                 return;
42617             }
42618             if (this.cblack.indexOf(tag) > -1) {
42619                 return;
42620             }
42621             this.cblack.push(tag);
42622             
42623         }, this);
42624     },
42625     
42626     setStylesheets : function(stylesheets)
42627     {
42628         if(typeof(stylesheets) == 'string'){
42629             Roo.get(this.iframe.contentDocument.head).createChild({
42630                 tag : 'link',
42631                 rel : 'stylesheet',
42632                 type : 'text/css',
42633                 href : stylesheets
42634             });
42635             
42636             return;
42637         }
42638         var _this = this;
42639      
42640         Roo.each(stylesheets, function(s) {
42641             if(!s.length){
42642                 return;
42643             }
42644             
42645             Roo.get(_this.iframe.contentDocument.head).createChild({
42646                 tag : 'link',
42647                 rel : 'stylesheet',
42648                 type : 'text/css',
42649                 href : s
42650             });
42651         });
42652
42653         
42654     },
42655     
42656     removeStylesheets : function()
42657     {
42658         var _this = this;
42659         
42660         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42661             s.remove();
42662         });
42663     }
42664     
42665     // hide stuff that is not compatible
42666     /**
42667      * @event blur
42668      * @hide
42669      */
42670     /**
42671      * @event change
42672      * @hide
42673      */
42674     /**
42675      * @event focus
42676      * @hide
42677      */
42678     /**
42679      * @event specialkey
42680      * @hide
42681      */
42682     /**
42683      * @cfg {String} fieldClass @hide
42684      */
42685     /**
42686      * @cfg {String} focusClass @hide
42687      */
42688     /**
42689      * @cfg {String} autoCreate @hide
42690      */
42691     /**
42692      * @cfg {String} inputType @hide
42693      */
42694     /**
42695      * @cfg {String} invalidClass @hide
42696      */
42697     /**
42698      * @cfg {String} invalidText @hide
42699      */
42700     /**
42701      * @cfg {String} msgFx @hide
42702      */
42703     /**
42704      * @cfg {String} validateOnBlur @hide
42705      */
42706 });
42707
42708 Roo.HtmlEditorCore.white = [
42709         'area', 'br', 'img', 'input', 'hr', 'wbr',
42710         
42711        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42712        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42713        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42714        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42715        'table',   'ul',         'xmp', 
42716        
42717        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42718       'thead',   'tr', 
42719      
42720       'dir', 'menu', 'ol', 'ul', 'dl',
42721        
42722       'embed',  'object'
42723 ];
42724
42725
42726 Roo.HtmlEditorCore.black = [
42727     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42728         'applet', // 
42729         'base',   'basefont', 'bgsound', 'blink',  'body', 
42730         'frame',  'frameset', 'head',    'html',   'ilayer', 
42731         'iframe', 'layer',  'link',     'meta',    'object',   
42732         'script', 'style' ,'title',  'xml' // clean later..
42733 ];
42734 Roo.HtmlEditorCore.clean = [
42735     'script', 'style', 'title', 'xml'
42736 ];
42737 Roo.HtmlEditorCore.remove = [
42738     'font'
42739 ];
42740 // attributes..
42741
42742 Roo.HtmlEditorCore.ablack = [
42743     'on'
42744 ];
42745     
42746 Roo.HtmlEditorCore.aclean = [ 
42747     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42748 ];
42749
42750 // protocols..
42751 Roo.HtmlEditorCore.pwhite= [
42752         'http',  'https',  'mailto'
42753 ];
42754
42755 // white listed style attributes.
42756 Roo.HtmlEditorCore.cwhite= [
42757       //  'text-align', /// default is to allow most things..
42758       
42759          
42760 //        'font-size'//??
42761 ];
42762
42763 // black listed style attributes.
42764 Roo.HtmlEditorCore.cblack= [
42765       //  'font-size' -- this can be set by the project 
42766 ];
42767
42768
42769 Roo.HtmlEditorCore.swapCodes   =[ 
42770     [    8211, "--" ], 
42771     [    8212, "--" ], 
42772     [    8216,  "'" ],  
42773     [    8217, "'" ],  
42774     [    8220, '"' ],  
42775     [    8221, '"' ],  
42776     [    8226, "*" ],  
42777     [    8230, "..." ]
42778 ]; 
42779
42780     //<script type="text/javascript">
42781
42782 /*
42783  * Ext JS Library 1.1.1
42784  * Copyright(c) 2006-2007, Ext JS, LLC.
42785  * Licence LGPL
42786  * 
42787  */
42788  
42789  
42790 Roo.form.HtmlEditor = function(config){
42791     
42792     
42793     
42794     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42795     
42796     if (!this.toolbars) {
42797         this.toolbars = [];
42798     }
42799     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42800     
42801     
42802 };
42803
42804 /**
42805  * @class Roo.form.HtmlEditor
42806  * @extends Roo.form.Field
42807  * Provides a lightweight HTML Editor component.
42808  *
42809  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42810  * 
42811  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42812  * supported by this editor.</b><br/><br/>
42813  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42814  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42815  */
42816 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42817     /**
42818      * @cfg {Boolean} clearUp
42819      */
42820     clearUp : true,
42821       /**
42822      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42823      */
42824     toolbars : false,
42825    
42826      /**
42827      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42828      *                        Roo.resizable.
42829      */
42830     resizable : false,
42831      /**
42832      * @cfg {Number} height (in pixels)
42833      */   
42834     height: 300,
42835    /**
42836      * @cfg {Number} width (in pixels)
42837      */   
42838     width: 500,
42839     
42840     /**
42841      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42842      * 
42843      */
42844     stylesheets: false,
42845     
42846     
42847      /**
42848      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42849      * 
42850      */
42851     cblack: false,
42852     /**
42853      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42854      * 
42855      */
42856     cwhite: false,
42857     
42858      /**
42859      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42860      * 
42861      */
42862     black: false,
42863     /**
42864      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42865      * 
42866      */
42867     white: false,
42868     
42869     // id of frame..
42870     frameId: false,
42871     
42872     // private properties
42873     validationEvent : false,
42874     deferHeight: true,
42875     initialized : false,
42876     activated : false,
42877     
42878     onFocus : Roo.emptyFn,
42879     iframePad:3,
42880     hideMode:'offsets',
42881     
42882     actionMode : 'container', // defaults to hiding it...
42883     
42884     defaultAutoCreate : { // modified by initCompnoent..
42885         tag: "textarea",
42886         style:"width:500px;height:300px;",
42887         autocomplete: "new-password"
42888     },
42889
42890     // private
42891     initComponent : function(){
42892         this.addEvents({
42893             /**
42894              * @event initialize
42895              * Fires when the editor is fully initialized (including the iframe)
42896              * @param {HtmlEditor} this
42897              */
42898             initialize: true,
42899             /**
42900              * @event activate
42901              * Fires when the editor is first receives the focus. Any insertion must wait
42902              * until after this event.
42903              * @param {HtmlEditor} this
42904              */
42905             activate: true,
42906              /**
42907              * @event beforesync
42908              * Fires before the textarea is updated with content from the editor iframe. Return false
42909              * to cancel the sync.
42910              * @param {HtmlEditor} this
42911              * @param {String} html
42912              */
42913             beforesync: true,
42914              /**
42915              * @event beforepush
42916              * Fires before the iframe editor is updated with content from the textarea. Return false
42917              * to cancel the push.
42918              * @param {HtmlEditor} this
42919              * @param {String} html
42920              */
42921             beforepush: true,
42922              /**
42923              * @event sync
42924              * Fires when the textarea is updated with content from the editor iframe.
42925              * @param {HtmlEditor} this
42926              * @param {String} html
42927              */
42928             sync: true,
42929              /**
42930              * @event push
42931              * Fires when the iframe editor is updated with content from the textarea.
42932              * @param {HtmlEditor} this
42933              * @param {String} html
42934              */
42935             push: true,
42936              /**
42937              * @event editmodechange
42938              * Fires when the editor switches edit modes
42939              * @param {HtmlEditor} this
42940              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42941              */
42942             editmodechange: true,
42943             /**
42944              * @event editorevent
42945              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42946              * @param {HtmlEditor} this
42947              */
42948             editorevent: true,
42949             /**
42950              * @event firstfocus
42951              * Fires when on first focus - needed by toolbars..
42952              * @param {HtmlEditor} this
42953              */
42954             firstfocus: true,
42955             /**
42956              * @event autosave
42957              * Auto save the htmlEditor value as a file into Events
42958              * @param {HtmlEditor} this
42959              */
42960             autosave: true,
42961             /**
42962              * @event savedpreview
42963              * preview the saved version of htmlEditor
42964              * @param {HtmlEditor} this
42965              */
42966             savedpreview: true,
42967             
42968             /**
42969             * @event stylesheetsclick
42970             * Fires when press the Sytlesheets button
42971             * @param {Roo.HtmlEditorCore} this
42972             */
42973             stylesheetsclick: true
42974         });
42975         this.defaultAutoCreate =  {
42976             tag: "textarea",
42977             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42978             autocomplete: "new-password"
42979         };
42980     },
42981
42982     /**
42983      * Protected method that will not generally be called directly. It
42984      * is called when the editor creates its toolbar. Override this method if you need to
42985      * add custom toolbar buttons.
42986      * @param {HtmlEditor} editor
42987      */
42988     createToolbar : function(editor){
42989         Roo.log("create toolbars");
42990         if (!editor.toolbars || !editor.toolbars.length) {
42991             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42992         }
42993         
42994         for (var i =0 ; i < editor.toolbars.length;i++) {
42995             editor.toolbars[i] = Roo.factory(
42996                     typeof(editor.toolbars[i]) == 'string' ?
42997                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42998                 Roo.form.HtmlEditor);
42999             editor.toolbars[i].init(editor);
43000         }
43001          
43002         
43003     },
43004
43005      
43006     // private
43007     onRender : function(ct, position)
43008     {
43009         var _t = this;
43010         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
43011         
43012         this.wrap = this.el.wrap({
43013             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
43014         });
43015         
43016         this.editorcore.onRender(ct, position);
43017          
43018         if (this.resizable) {
43019             this.resizeEl = new Roo.Resizable(this.wrap, {
43020                 pinned : true,
43021                 wrap: true,
43022                 dynamic : true,
43023                 minHeight : this.height,
43024                 height: this.height,
43025                 handles : this.resizable,
43026                 width: this.width,
43027                 listeners : {
43028                     resize : function(r, w, h) {
43029                         _t.onResize(w,h); // -something
43030                     }
43031                 }
43032             });
43033             
43034         }
43035         this.createToolbar(this);
43036        
43037         
43038         if(!this.width){
43039             this.setSize(this.wrap.getSize());
43040         }
43041         if (this.resizeEl) {
43042             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43043             // should trigger onReize..
43044         }
43045         
43046         this.keyNav = new Roo.KeyNav(this.el, {
43047             
43048             "tab" : function(e){
43049                 e.preventDefault();
43050                 
43051                 var value = this.getValue();
43052                 
43053                 var start = this.el.dom.selectionStart;
43054                 var end = this.el.dom.selectionEnd;
43055                 
43056                 if(!e.shiftKey){
43057                     
43058                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43059                     this.el.dom.setSelectionRange(end + 1, end + 1);
43060                     return;
43061                 }
43062                 
43063                 var f = value.substring(0, start).split("\t");
43064                 
43065                 if(f.pop().length != 0){
43066                     return;
43067                 }
43068                 
43069                 this.setValue(f.join("\t") + value.substring(end));
43070                 this.el.dom.setSelectionRange(start - 1, start - 1);
43071                 
43072             },
43073             
43074             "home" : function(e){
43075                 e.preventDefault();
43076                 
43077                 var curr = this.el.dom.selectionStart;
43078                 var lines = this.getValue().split("\n");
43079                 
43080                 if(!lines.length){
43081                     return;
43082                 }
43083                 
43084                 if(e.ctrlKey){
43085                     this.el.dom.setSelectionRange(0, 0);
43086                     return;
43087                 }
43088                 
43089                 var pos = 0;
43090                 
43091                 for (var i = 0; i < lines.length;i++) {
43092                     pos += lines[i].length;
43093                     
43094                     if(i != 0){
43095                         pos += 1;
43096                     }
43097                     
43098                     if(pos < curr){
43099                         continue;
43100                     }
43101                     
43102                     pos -= lines[i].length;
43103                     
43104                     break;
43105                 }
43106                 
43107                 if(!e.shiftKey){
43108                     this.el.dom.setSelectionRange(pos, pos);
43109                     return;
43110                 }
43111                 
43112                 this.el.dom.selectionStart = pos;
43113                 this.el.dom.selectionEnd = curr;
43114             },
43115             
43116             "end" : function(e){
43117                 e.preventDefault();
43118                 
43119                 var curr = this.el.dom.selectionStart;
43120                 var lines = this.getValue().split("\n");
43121                 
43122                 if(!lines.length){
43123                     return;
43124                 }
43125                 
43126                 if(e.ctrlKey){
43127                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43128                     return;
43129                 }
43130                 
43131                 var pos = 0;
43132                 
43133                 for (var i = 0; i < lines.length;i++) {
43134                     
43135                     pos += lines[i].length;
43136                     
43137                     if(i != 0){
43138                         pos += 1;
43139                     }
43140                     
43141                     if(pos < curr){
43142                         continue;
43143                     }
43144                     
43145                     break;
43146                 }
43147                 
43148                 if(!e.shiftKey){
43149                     this.el.dom.setSelectionRange(pos, pos);
43150                     return;
43151                 }
43152                 
43153                 this.el.dom.selectionStart = curr;
43154                 this.el.dom.selectionEnd = pos;
43155             },
43156
43157             scope : this,
43158
43159             doRelay : function(foo, bar, hname){
43160                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43161             },
43162
43163             forceKeyDown: true
43164         });
43165         
43166 //        if(this.autosave && this.w){
43167 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43168 //        }
43169     },
43170
43171     // private
43172     onResize : function(w, h)
43173     {
43174         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43175         var ew = false;
43176         var eh = false;
43177         
43178         if(this.el ){
43179             if(typeof w == 'number'){
43180                 var aw = w - this.wrap.getFrameWidth('lr');
43181                 this.el.setWidth(this.adjustWidth('textarea', aw));
43182                 ew = aw;
43183             }
43184             if(typeof h == 'number'){
43185                 var tbh = 0;
43186                 for (var i =0; i < this.toolbars.length;i++) {
43187                     // fixme - ask toolbars for heights?
43188                     tbh += this.toolbars[i].tb.el.getHeight();
43189                     if (this.toolbars[i].footer) {
43190                         tbh += this.toolbars[i].footer.el.getHeight();
43191                     }
43192                 }
43193                 
43194                 
43195                 
43196                 
43197                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43198                 ah -= 5; // knock a few pixes off for look..
43199 //                Roo.log(ah);
43200                 this.el.setHeight(this.adjustWidth('textarea', ah));
43201                 var eh = ah;
43202             }
43203         }
43204         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43205         this.editorcore.onResize(ew,eh);
43206         
43207     },
43208
43209     /**
43210      * Toggles the editor between standard and source edit mode.
43211      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43212      */
43213     toggleSourceEdit : function(sourceEditMode)
43214     {
43215         this.editorcore.toggleSourceEdit(sourceEditMode);
43216         
43217         if(this.editorcore.sourceEditMode){
43218             Roo.log('editor - showing textarea');
43219             
43220 //            Roo.log('in');
43221 //            Roo.log(this.syncValue());
43222             this.editorcore.syncValue();
43223             this.el.removeClass('x-hidden');
43224             this.el.dom.removeAttribute('tabIndex');
43225             this.el.focus();
43226             
43227             for (var i = 0; i < this.toolbars.length; i++) {
43228                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43229                     this.toolbars[i].tb.hide();
43230                     this.toolbars[i].footer.hide();
43231                 }
43232             }
43233             
43234         }else{
43235             Roo.log('editor - hiding textarea');
43236 //            Roo.log('out')
43237 //            Roo.log(this.pushValue()); 
43238             this.editorcore.pushValue();
43239             
43240             this.el.addClass('x-hidden');
43241             this.el.dom.setAttribute('tabIndex', -1);
43242             
43243             for (var i = 0; i < this.toolbars.length; i++) {
43244                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43245                     this.toolbars[i].tb.show();
43246                     this.toolbars[i].footer.show();
43247                 }
43248             }
43249             
43250             //this.deferFocus();
43251         }
43252         
43253         this.setSize(this.wrap.getSize());
43254         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43255         
43256         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43257     },
43258  
43259     // private (for BoxComponent)
43260     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43261
43262     // private (for BoxComponent)
43263     getResizeEl : function(){
43264         return this.wrap;
43265     },
43266
43267     // private (for BoxComponent)
43268     getPositionEl : function(){
43269         return this.wrap;
43270     },
43271
43272     // private
43273     initEvents : function(){
43274         this.originalValue = this.getValue();
43275     },
43276
43277     /**
43278      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43279      * @method
43280      */
43281     markInvalid : Roo.emptyFn,
43282     /**
43283      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43284      * @method
43285      */
43286     clearInvalid : Roo.emptyFn,
43287
43288     setValue : function(v){
43289         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43290         this.editorcore.pushValue();
43291     },
43292
43293      
43294     // private
43295     deferFocus : function(){
43296         this.focus.defer(10, this);
43297     },
43298
43299     // doc'ed in Field
43300     focus : function(){
43301         this.editorcore.focus();
43302         
43303     },
43304       
43305
43306     // private
43307     onDestroy : function(){
43308         
43309         
43310         
43311         if(this.rendered){
43312             
43313             for (var i =0; i < this.toolbars.length;i++) {
43314                 // fixme - ask toolbars for heights?
43315                 this.toolbars[i].onDestroy();
43316             }
43317             
43318             this.wrap.dom.innerHTML = '';
43319             this.wrap.remove();
43320         }
43321     },
43322
43323     // private
43324     onFirstFocus : function(){
43325         //Roo.log("onFirstFocus");
43326         this.editorcore.onFirstFocus();
43327          for (var i =0; i < this.toolbars.length;i++) {
43328             this.toolbars[i].onFirstFocus();
43329         }
43330         
43331     },
43332     
43333     // private
43334     syncValue : function()
43335     {
43336         this.editorcore.syncValue();
43337     },
43338     
43339     pushValue : function()
43340     {
43341         this.editorcore.pushValue();
43342     },
43343     
43344     setStylesheets : function(stylesheets)
43345     {
43346         this.editorcore.setStylesheets(stylesheets);
43347     },
43348     
43349     removeStylesheets : function()
43350     {
43351         this.editorcore.removeStylesheets();
43352     }
43353      
43354     
43355     // hide stuff that is not compatible
43356     /**
43357      * @event blur
43358      * @hide
43359      */
43360     /**
43361      * @event change
43362      * @hide
43363      */
43364     /**
43365      * @event focus
43366      * @hide
43367      */
43368     /**
43369      * @event specialkey
43370      * @hide
43371      */
43372     /**
43373      * @cfg {String} fieldClass @hide
43374      */
43375     /**
43376      * @cfg {String} focusClass @hide
43377      */
43378     /**
43379      * @cfg {String} autoCreate @hide
43380      */
43381     /**
43382      * @cfg {String} inputType @hide
43383      */
43384     /**
43385      * @cfg {String} invalidClass @hide
43386      */
43387     /**
43388      * @cfg {String} invalidText @hide
43389      */
43390     /**
43391      * @cfg {String} msgFx @hide
43392      */
43393     /**
43394      * @cfg {String} validateOnBlur @hide
43395      */
43396 });
43397  
43398     // <script type="text/javascript">
43399 /*
43400  * Based on
43401  * Ext JS Library 1.1.1
43402  * Copyright(c) 2006-2007, Ext JS, LLC.
43403  *  
43404  
43405  */
43406
43407 /**
43408  * @class Roo.form.HtmlEditorToolbar1
43409  * Basic Toolbar
43410  * 
43411  * Usage:
43412  *
43413  new Roo.form.HtmlEditor({
43414     ....
43415     toolbars : [
43416         new Roo.form.HtmlEditorToolbar1({
43417             disable : { fonts: 1 , format: 1, ..., ... , ...],
43418             btns : [ .... ]
43419         })
43420     }
43421      
43422  * 
43423  * @cfg {Object} disable List of elements to disable..
43424  * @cfg {Array} btns List of additional buttons.
43425  * 
43426  * 
43427  * NEEDS Extra CSS? 
43428  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43429  */
43430  
43431 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43432 {
43433     
43434     Roo.apply(this, config);
43435     
43436     // default disabled, based on 'good practice'..
43437     this.disable = this.disable || {};
43438     Roo.applyIf(this.disable, {
43439         fontSize : true,
43440         colors : true,
43441         specialElements : true
43442     });
43443     
43444     
43445     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43446     // dont call parent... till later.
43447 }
43448
43449 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43450     
43451     tb: false,
43452     
43453     rendered: false,
43454     
43455     editor : false,
43456     editorcore : false,
43457     /**
43458      * @cfg {Object} disable  List of toolbar elements to disable
43459          
43460      */
43461     disable : false,
43462     
43463     
43464      /**
43465      * @cfg {String} createLinkText The default text for the create link prompt
43466      */
43467     createLinkText : 'Please enter the URL for the link:',
43468     /**
43469      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43470      */
43471     defaultLinkValue : 'http:/'+'/',
43472    
43473     
43474       /**
43475      * @cfg {Array} fontFamilies An array of available font families
43476      */
43477     fontFamilies : [
43478         'Arial',
43479         'Courier New',
43480         'Tahoma',
43481         'Times New Roman',
43482         'Verdana'
43483     ],
43484     
43485     specialChars : [
43486            "&#169;",
43487           "&#174;",     
43488           "&#8482;",    
43489           "&#163;" ,    
43490          // "&#8212;",    
43491           "&#8230;",    
43492           "&#247;" ,    
43493         //  "&#225;" ,     ?? a acute?
43494            "&#8364;"    , //Euro
43495        //   "&#8220;"    ,
43496         //  "&#8221;"    ,
43497         //  "&#8226;"    ,
43498           "&#176;"  //   , // degrees
43499
43500          // "&#233;"     , // e ecute
43501          // "&#250;"     , // u ecute?
43502     ],
43503     
43504     specialElements : [
43505         {
43506             text: "Insert Table",
43507             xtype: 'MenuItem',
43508             xns : Roo.Menu,
43509             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43510                 
43511         },
43512         {    
43513             text: "Insert Image",
43514             xtype: 'MenuItem',
43515             xns : Roo.Menu,
43516             ihtml : '<img src="about:blank"/>'
43517             
43518         }
43519         
43520          
43521     ],
43522     
43523     
43524     inputElements : [ 
43525             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43526             "input:submit", "input:button", "select", "textarea", "label" ],
43527     formats : [
43528         ["p"] ,  
43529         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43530         ["pre"],[ "code"], 
43531         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43532         ['div'],['span']
43533     ],
43534     
43535     cleanStyles : [
43536         "font-size"
43537     ],
43538      /**
43539      * @cfg {String} defaultFont default font to use.
43540      */
43541     defaultFont: 'tahoma',
43542    
43543     fontSelect : false,
43544     
43545     
43546     formatCombo : false,
43547     
43548     init : function(editor)
43549     {
43550         this.editor = editor;
43551         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43552         var editorcore = this.editorcore;
43553         
43554         var _t = this;
43555         
43556         var fid = editorcore.frameId;
43557         var etb = this;
43558         function btn(id, toggle, handler){
43559             var xid = fid + '-'+ id ;
43560             return {
43561                 id : xid,
43562                 cmd : id,
43563                 cls : 'x-btn-icon x-edit-'+id,
43564                 enableToggle:toggle !== false,
43565                 scope: _t, // was editor...
43566                 handler:handler||_t.relayBtnCmd,
43567                 clickEvent:'mousedown',
43568                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43569                 tabIndex:-1
43570             };
43571         }
43572         
43573         
43574         
43575         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43576         this.tb = tb;
43577          // stop form submits
43578         tb.el.on('click', function(e){
43579             e.preventDefault(); // what does this do?
43580         });
43581
43582         if(!this.disable.font) { // && !Roo.isSafari){
43583             /* why no safari for fonts 
43584             editor.fontSelect = tb.el.createChild({
43585                 tag:'select',
43586                 tabIndex: -1,
43587                 cls:'x-font-select',
43588                 html: this.createFontOptions()
43589             });
43590             
43591             editor.fontSelect.on('change', function(){
43592                 var font = editor.fontSelect.dom.value;
43593                 editor.relayCmd('fontname', font);
43594                 editor.deferFocus();
43595             }, editor);
43596             
43597             tb.add(
43598                 editor.fontSelect.dom,
43599                 '-'
43600             );
43601             */
43602             
43603         };
43604         if(!this.disable.formats){
43605             this.formatCombo = new Roo.form.ComboBox({
43606                 store: new Roo.data.SimpleStore({
43607                     id : 'tag',
43608                     fields: ['tag'],
43609                     data : this.formats // from states.js
43610                 }),
43611                 blockFocus : true,
43612                 name : '',
43613                 //autoCreate : {tag: "div",  size: "20"},
43614                 displayField:'tag',
43615                 typeAhead: false,
43616                 mode: 'local',
43617                 editable : false,
43618                 triggerAction: 'all',
43619                 emptyText:'Add tag',
43620                 selectOnFocus:true,
43621                 width:135,
43622                 listeners : {
43623                     'select': function(c, r, i) {
43624                         editorcore.insertTag(r.get('tag'));
43625                         editor.focus();
43626                     }
43627                 }
43628
43629             });
43630             tb.addField(this.formatCombo);
43631             
43632         }
43633         
43634         if(!this.disable.format){
43635             tb.add(
43636                 btn('bold'),
43637                 btn('italic'),
43638                 btn('underline'),
43639                 btn('strikethrough')
43640             );
43641         };
43642         if(!this.disable.fontSize){
43643             tb.add(
43644                 '-',
43645                 
43646                 
43647                 btn('increasefontsize', false, editorcore.adjustFont),
43648                 btn('decreasefontsize', false, editorcore.adjustFont)
43649             );
43650         };
43651         
43652         
43653         if(!this.disable.colors){
43654             tb.add(
43655                 '-', {
43656                     id:editorcore.frameId +'-forecolor',
43657                     cls:'x-btn-icon x-edit-forecolor',
43658                     clickEvent:'mousedown',
43659                     tooltip: this.buttonTips['forecolor'] || undefined,
43660                     tabIndex:-1,
43661                     menu : new Roo.menu.ColorMenu({
43662                         allowReselect: true,
43663                         focus: Roo.emptyFn,
43664                         value:'000000',
43665                         plain:true,
43666                         selectHandler: function(cp, color){
43667                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43668                             editor.deferFocus();
43669                         },
43670                         scope: editorcore,
43671                         clickEvent:'mousedown'
43672                     })
43673                 }, {
43674                     id:editorcore.frameId +'backcolor',
43675                     cls:'x-btn-icon x-edit-backcolor',
43676                     clickEvent:'mousedown',
43677                     tooltip: this.buttonTips['backcolor'] || undefined,
43678                     tabIndex:-1,
43679                     menu : new Roo.menu.ColorMenu({
43680                         focus: Roo.emptyFn,
43681                         value:'FFFFFF',
43682                         plain:true,
43683                         allowReselect: true,
43684                         selectHandler: function(cp, color){
43685                             if(Roo.isGecko){
43686                                 editorcore.execCmd('useCSS', false);
43687                                 editorcore.execCmd('hilitecolor', color);
43688                                 editorcore.execCmd('useCSS', true);
43689                                 editor.deferFocus();
43690                             }else{
43691                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43692                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43693                                 editor.deferFocus();
43694                             }
43695                         },
43696                         scope:editorcore,
43697                         clickEvent:'mousedown'
43698                     })
43699                 }
43700             );
43701         };
43702         // now add all the items...
43703         
43704
43705         if(!this.disable.alignments){
43706             tb.add(
43707                 '-',
43708                 btn('justifyleft'),
43709                 btn('justifycenter'),
43710                 btn('justifyright')
43711             );
43712         };
43713
43714         //if(!Roo.isSafari){
43715             if(!this.disable.links){
43716                 tb.add(
43717                     '-',
43718                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43719                 );
43720             };
43721
43722             if(!this.disable.lists){
43723                 tb.add(
43724                     '-',
43725                     btn('insertorderedlist'),
43726                     btn('insertunorderedlist')
43727                 );
43728             }
43729             if(!this.disable.sourceEdit){
43730                 tb.add(
43731                     '-',
43732                     btn('sourceedit', true, function(btn){
43733                         this.toggleSourceEdit(btn.pressed);
43734                     })
43735                 );
43736             }
43737         //}
43738         
43739         var smenu = { };
43740         // special menu.. - needs to be tidied up..
43741         if (!this.disable.special) {
43742             smenu = {
43743                 text: "&#169;",
43744                 cls: 'x-edit-none',
43745                 
43746                 menu : {
43747                     items : []
43748                 }
43749             };
43750             for (var i =0; i < this.specialChars.length; i++) {
43751                 smenu.menu.items.push({
43752                     
43753                     html: this.specialChars[i],
43754                     handler: function(a,b) {
43755                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43756                         //editor.insertAtCursor(a.html);
43757                         
43758                     },
43759                     tabIndex:-1
43760                 });
43761             }
43762             
43763             
43764             tb.add(smenu);
43765             
43766             
43767         }
43768         
43769         var cmenu = { };
43770         if (!this.disable.cleanStyles) {
43771             cmenu = {
43772                 cls: 'x-btn-icon x-btn-clear',
43773                 
43774                 menu : {
43775                     items : []
43776                 }
43777             };
43778             for (var i =0; i < this.cleanStyles.length; i++) {
43779                 cmenu.menu.items.push({
43780                     actiontype : this.cleanStyles[i],
43781                     html: 'Remove ' + this.cleanStyles[i],
43782                     handler: function(a,b) {
43783 //                        Roo.log(a);
43784 //                        Roo.log(b);
43785                         var c = Roo.get(editorcore.doc.body);
43786                         c.select('[style]').each(function(s) {
43787                             s.dom.style.removeProperty(a.actiontype);
43788                         });
43789                         editorcore.syncValue();
43790                     },
43791                     tabIndex:-1
43792                 });
43793             }
43794              cmenu.menu.items.push({
43795                 actiontype : 'tablewidths',
43796                 html: 'Remove Table Widths',
43797                 handler: function(a,b) {
43798                     editorcore.cleanTableWidths();
43799                     editorcore.syncValue();
43800                 },
43801                 tabIndex:-1
43802             });
43803             cmenu.menu.items.push({
43804                 actiontype : 'word',
43805                 html: 'Remove MS Word Formating',
43806                 handler: function(a,b) {
43807                     editorcore.cleanWord();
43808                     editorcore.syncValue();
43809                 },
43810                 tabIndex:-1
43811             });
43812             
43813             cmenu.menu.items.push({
43814                 actiontype : 'all',
43815                 html: 'Remove All Styles',
43816                 handler: function(a,b) {
43817                     
43818                     var c = Roo.get(editorcore.doc.body);
43819                     c.select('[style]').each(function(s) {
43820                         s.dom.removeAttribute('style');
43821                     });
43822                     editorcore.syncValue();
43823                 },
43824                 tabIndex:-1
43825             });
43826             
43827             cmenu.menu.items.push({
43828                 actiontype : 'all',
43829                 html: 'Remove All CSS Classes',
43830                 handler: function(a,b) {
43831                     
43832                     var c = Roo.get(editorcore.doc.body);
43833                     c.select('[class]').each(function(s) {
43834                         s.dom.className = '';
43835                     });
43836                     editorcore.syncValue();
43837                 },
43838                 tabIndex:-1
43839             });
43840             
43841              cmenu.menu.items.push({
43842                 actiontype : 'tidy',
43843                 html: 'Tidy HTML Source',
43844                 handler: function(a,b) {
43845                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43846                     editorcore.syncValue();
43847                 },
43848                 tabIndex:-1
43849             });
43850             
43851             
43852             tb.add(cmenu);
43853         }
43854          
43855         if (!this.disable.specialElements) {
43856             var semenu = {
43857                 text: "Other;",
43858                 cls: 'x-edit-none',
43859                 menu : {
43860                     items : []
43861                 }
43862             };
43863             for (var i =0; i < this.specialElements.length; i++) {
43864                 semenu.menu.items.push(
43865                     Roo.apply({ 
43866                         handler: function(a,b) {
43867                             editor.insertAtCursor(this.ihtml);
43868                         }
43869                     }, this.specialElements[i])
43870                 );
43871                     
43872             }
43873             
43874             tb.add(semenu);
43875             
43876             
43877         }
43878          
43879         
43880         if (this.btns) {
43881             for(var i =0; i< this.btns.length;i++) {
43882                 var b = Roo.factory(this.btns[i],Roo.form);
43883                 b.cls =  'x-edit-none';
43884                 
43885                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43886                     b.cls += ' x-init-enable';
43887                 }
43888                 
43889                 b.scope = editorcore;
43890                 tb.add(b);
43891             }
43892         
43893         }
43894         
43895         
43896         
43897         // disable everything...
43898         
43899         this.tb.items.each(function(item){
43900             
43901            if(
43902                 item.id != editorcore.frameId+ '-sourceedit' && 
43903                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43904             ){
43905                 
43906                 item.disable();
43907             }
43908         });
43909         this.rendered = true;
43910         
43911         // the all the btns;
43912         editor.on('editorevent', this.updateToolbar, this);
43913         // other toolbars need to implement this..
43914         //editor.on('editmodechange', this.updateToolbar, this);
43915     },
43916     
43917     
43918     relayBtnCmd : function(btn) {
43919         this.editorcore.relayCmd(btn.cmd);
43920     },
43921     // private used internally
43922     createLink : function(){
43923         Roo.log("create link?");
43924         var url = prompt(this.createLinkText, this.defaultLinkValue);
43925         if(url && url != 'http:/'+'/'){
43926             this.editorcore.relayCmd('createlink', url);
43927         }
43928     },
43929
43930     
43931     /**
43932      * Protected method that will not generally be called directly. It triggers
43933      * a toolbar update by reading the markup state of the current selection in the editor.
43934      */
43935     updateToolbar: function(){
43936
43937         if(!this.editorcore.activated){
43938             this.editor.onFirstFocus();
43939             return;
43940         }
43941
43942         var btns = this.tb.items.map, 
43943             doc = this.editorcore.doc,
43944             frameId = this.editorcore.frameId;
43945
43946         if(!this.disable.font && !Roo.isSafari){
43947             /*
43948             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43949             if(name != this.fontSelect.dom.value){
43950                 this.fontSelect.dom.value = name;
43951             }
43952             */
43953         }
43954         if(!this.disable.format){
43955             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43956             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43957             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43958             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
43959         }
43960         if(!this.disable.alignments){
43961             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43962             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43963             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43964         }
43965         if(!Roo.isSafari && !this.disable.lists){
43966             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43967             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43968         }
43969         
43970         var ans = this.editorcore.getAllAncestors();
43971         if (this.formatCombo) {
43972             
43973             
43974             var store = this.formatCombo.store;
43975             this.formatCombo.setValue("");
43976             for (var i =0; i < ans.length;i++) {
43977                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43978                     // select it..
43979                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43980                     break;
43981                 }
43982             }
43983         }
43984         
43985         
43986         
43987         // hides menus... - so this cant be on a menu...
43988         Roo.menu.MenuMgr.hideAll();
43989
43990         //this.editorsyncValue();
43991     },
43992    
43993     
43994     createFontOptions : function(){
43995         var buf = [], fs = this.fontFamilies, ff, lc;
43996         
43997         
43998         
43999         for(var i = 0, len = fs.length; i< len; i++){
44000             ff = fs[i];
44001             lc = ff.toLowerCase();
44002             buf.push(
44003                 '<option value="',lc,'" style="font-family:',ff,';"',
44004                     (this.defaultFont == lc ? ' selected="true">' : '>'),
44005                     ff,
44006                 '</option>'
44007             );
44008         }
44009         return buf.join('');
44010     },
44011     
44012     toggleSourceEdit : function(sourceEditMode){
44013         
44014         Roo.log("toolbar toogle");
44015         if(sourceEditMode === undefined){
44016             sourceEditMode = !this.sourceEditMode;
44017         }
44018         this.sourceEditMode = sourceEditMode === true;
44019         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
44020         // just toggle the button?
44021         if(btn.pressed !== this.sourceEditMode){
44022             btn.toggle(this.sourceEditMode);
44023             return;
44024         }
44025         
44026         if(sourceEditMode){
44027             Roo.log("disabling buttons");
44028             this.tb.items.each(function(item){
44029                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
44030                     item.disable();
44031                 }
44032             });
44033           
44034         }else{
44035             Roo.log("enabling buttons");
44036             if(this.editorcore.initialized){
44037                 this.tb.items.each(function(item){
44038                     item.enable();
44039                 });
44040             }
44041             
44042         }
44043         Roo.log("calling toggole on editor");
44044         // tell the editor that it's been pressed..
44045         this.editor.toggleSourceEdit(sourceEditMode);
44046        
44047     },
44048      /**
44049      * Object collection of toolbar tooltips for the buttons in the editor. The key
44050      * is the command id associated with that button and the value is a valid QuickTips object.
44051      * For example:
44052 <pre><code>
44053 {
44054     bold : {
44055         title: 'Bold (Ctrl+B)',
44056         text: 'Make the selected text bold.',
44057         cls: 'x-html-editor-tip'
44058     },
44059     italic : {
44060         title: 'Italic (Ctrl+I)',
44061         text: 'Make the selected text italic.',
44062         cls: 'x-html-editor-tip'
44063     },
44064     ...
44065 </code></pre>
44066     * @type Object
44067      */
44068     buttonTips : {
44069         bold : {
44070             title: 'Bold (Ctrl+B)',
44071             text: 'Make the selected text bold.',
44072             cls: 'x-html-editor-tip'
44073         },
44074         italic : {
44075             title: 'Italic (Ctrl+I)',
44076             text: 'Make the selected text italic.',
44077             cls: 'x-html-editor-tip'
44078         },
44079         underline : {
44080             title: 'Underline (Ctrl+U)',
44081             text: 'Underline the selected text.',
44082             cls: 'x-html-editor-tip'
44083         },
44084         strikethrough : {
44085             title: 'Strikethrough',
44086             text: 'Strikethrough the selected text.',
44087             cls: 'x-html-editor-tip'
44088         },
44089         increasefontsize : {
44090             title: 'Grow Text',
44091             text: 'Increase the font size.',
44092             cls: 'x-html-editor-tip'
44093         },
44094         decreasefontsize : {
44095             title: 'Shrink Text',
44096             text: 'Decrease the font size.',
44097             cls: 'x-html-editor-tip'
44098         },
44099         backcolor : {
44100             title: 'Text Highlight Color',
44101             text: 'Change the background color of the selected text.',
44102             cls: 'x-html-editor-tip'
44103         },
44104         forecolor : {
44105             title: 'Font Color',
44106             text: 'Change the color of the selected text.',
44107             cls: 'x-html-editor-tip'
44108         },
44109         justifyleft : {
44110             title: 'Align Text Left',
44111             text: 'Align text to the left.',
44112             cls: 'x-html-editor-tip'
44113         },
44114         justifycenter : {
44115             title: 'Center Text',
44116             text: 'Center text in the editor.',
44117             cls: 'x-html-editor-tip'
44118         },
44119         justifyright : {
44120             title: 'Align Text Right',
44121             text: 'Align text to the right.',
44122             cls: 'x-html-editor-tip'
44123         },
44124         insertunorderedlist : {
44125             title: 'Bullet List',
44126             text: 'Start a bulleted list.',
44127             cls: 'x-html-editor-tip'
44128         },
44129         insertorderedlist : {
44130             title: 'Numbered List',
44131             text: 'Start a numbered list.',
44132             cls: 'x-html-editor-tip'
44133         },
44134         createlink : {
44135             title: 'Hyperlink',
44136             text: 'Make the selected text a hyperlink.',
44137             cls: 'x-html-editor-tip'
44138         },
44139         sourceedit : {
44140             title: 'Source Edit',
44141             text: 'Switch to source editing mode.',
44142             cls: 'x-html-editor-tip'
44143         }
44144     },
44145     // private
44146     onDestroy : function(){
44147         if(this.rendered){
44148             
44149             this.tb.items.each(function(item){
44150                 if(item.menu){
44151                     item.menu.removeAll();
44152                     if(item.menu.el){
44153                         item.menu.el.destroy();
44154                     }
44155                 }
44156                 item.destroy();
44157             });
44158              
44159         }
44160     },
44161     onFirstFocus: function() {
44162         this.tb.items.each(function(item){
44163            item.enable();
44164         });
44165     }
44166 });
44167
44168
44169
44170
44171 // <script type="text/javascript">
44172 /*
44173  * Based on
44174  * Ext JS Library 1.1.1
44175  * Copyright(c) 2006-2007, Ext JS, LLC.
44176  *  
44177  
44178  */
44179
44180  
44181 /**
44182  * @class Roo.form.HtmlEditor.ToolbarContext
44183  * Context Toolbar
44184  * 
44185  * Usage:
44186  *
44187  new Roo.form.HtmlEditor({
44188     ....
44189     toolbars : [
44190         { xtype: 'ToolbarStandard', styles : {} }
44191         { xtype: 'ToolbarContext', disable : {} }
44192     ]
44193 })
44194
44195      
44196  * 
44197  * @config : {Object} disable List of elements to disable.. (not done yet.)
44198  * @config : {Object} styles  Map of styles available.
44199  * 
44200  */
44201
44202 Roo.form.HtmlEditor.ToolbarContext = function(config)
44203 {
44204     
44205     Roo.apply(this, config);
44206     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44207     // dont call parent... till later.
44208     this.styles = this.styles || {};
44209 }
44210
44211  
44212
44213 Roo.form.HtmlEditor.ToolbarContext.types = {
44214     'IMG' : {
44215         width : {
44216             title: "Width",
44217             width: 40
44218         },
44219         height:  {
44220             title: "Height",
44221             width: 40
44222         },
44223         align: {
44224             title: "Align",
44225             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44226             width : 80
44227             
44228         },
44229         border: {
44230             title: "Border",
44231             width: 40
44232         },
44233         alt: {
44234             title: "Alt",
44235             width: 120
44236         },
44237         src : {
44238             title: "Src",
44239             width: 220
44240         }
44241         
44242     },
44243     'A' : {
44244         name : {
44245             title: "Name",
44246             width: 50
44247         },
44248         target:  {
44249             title: "Target",
44250             width: 120
44251         },
44252         href:  {
44253             title: "Href",
44254             width: 220
44255         } // border?
44256         
44257     },
44258     'TABLE' : {
44259         rows : {
44260             title: "Rows",
44261             width: 20
44262         },
44263         cols : {
44264             title: "Cols",
44265             width: 20
44266         },
44267         width : {
44268             title: "Width",
44269             width: 40
44270         },
44271         height : {
44272             title: "Height",
44273             width: 40
44274         },
44275         border : {
44276             title: "Border",
44277             width: 20
44278         }
44279     },
44280     'TD' : {
44281         width : {
44282             title: "Width",
44283             width: 40
44284         },
44285         height : {
44286             title: "Height",
44287             width: 40
44288         },   
44289         align: {
44290             title: "Align",
44291             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44292             width: 80
44293         },
44294         valign: {
44295             title: "Valign",
44296             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44297             width: 80
44298         },
44299         colspan: {
44300             title: "Colspan",
44301             width: 20
44302             
44303         },
44304          'font-family'  : {
44305             title : "Font",
44306             style : 'fontFamily',
44307             displayField: 'display',
44308             optname : 'font-family',
44309             width: 140
44310         }
44311     },
44312     'INPUT' : {
44313         name : {
44314             title: "name",
44315             width: 120
44316         },
44317         value : {
44318             title: "Value",
44319             width: 120
44320         },
44321         width : {
44322             title: "Width",
44323             width: 40
44324         }
44325     },
44326     'LABEL' : {
44327         'for' : {
44328             title: "For",
44329             width: 120
44330         }
44331     },
44332     'TEXTAREA' : {
44333           name : {
44334             title: "name",
44335             width: 120
44336         },
44337         rows : {
44338             title: "Rows",
44339             width: 20
44340         },
44341         cols : {
44342             title: "Cols",
44343             width: 20
44344         }
44345     },
44346     'SELECT' : {
44347         name : {
44348             title: "name",
44349             width: 120
44350         },
44351         selectoptions : {
44352             title: "Options",
44353             width: 200
44354         }
44355     },
44356     
44357     // should we really allow this??
44358     // should this just be 
44359     'BODY' : {
44360         title : {
44361             title: "Title",
44362             width: 200,
44363             disabled : true
44364         }
44365     },
44366     'SPAN' : {
44367         'font-family'  : {
44368             title : "Font",
44369             style : 'fontFamily',
44370             displayField: 'display',
44371             optname : 'font-family',
44372             width: 140
44373         }
44374     },
44375     'DIV' : {
44376         'font-family'  : {
44377             title : "Font",
44378             style : 'fontFamily',
44379             displayField: 'display',
44380             optname : 'font-family',
44381             width: 140
44382         }
44383     },
44384      'P' : {
44385         'font-family'  : {
44386             title : "Font",
44387             style : 'fontFamily',
44388             displayField: 'display',
44389             optname : 'font-family',
44390             width: 140
44391         }
44392     },
44393     
44394     '*' : {
44395         // empty..
44396     }
44397
44398 };
44399
44400 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44401 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44402
44403 Roo.form.HtmlEditor.ToolbarContext.options = {
44404         'font-family'  : [ 
44405                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44406                 [ 'Courier New', 'Courier New'],
44407                 [ 'Tahoma', 'Tahoma'],
44408                 [ 'Times New Roman,serif', 'Times'],
44409                 [ 'Verdana','Verdana' ]
44410         ]
44411 };
44412
44413 // fixme - these need to be configurable..
44414  
44415
44416 //Roo.form.HtmlEditor.ToolbarContext.types
44417
44418
44419 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44420     
44421     tb: false,
44422     
44423     rendered: false,
44424     
44425     editor : false,
44426     editorcore : false,
44427     /**
44428      * @cfg {Object} disable  List of toolbar elements to disable
44429          
44430      */
44431     disable : false,
44432     /**
44433      * @cfg {Object} styles List of styles 
44434      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44435      *
44436      * These must be defined in the page, so they get rendered correctly..
44437      * .headline { }
44438      * TD.underline { }
44439      * 
44440      */
44441     styles : false,
44442     
44443     options: false,
44444     
44445     toolbars : false,
44446     
44447     init : function(editor)
44448     {
44449         this.editor = editor;
44450         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44451         var editorcore = this.editorcore;
44452         
44453         var fid = editorcore.frameId;
44454         var etb = this;
44455         function btn(id, toggle, handler){
44456             var xid = fid + '-'+ id ;
44457             return {
44458                 id : xid,
44459                 cmd : id,
44460                 cls : 'x-btn-icon x-edit-'+id,
44461                 enableToggle:toggle !== false,
44462                 scope: editorcore, // was editor...
44463                 handler:handler||editorcore.relayBtnCmd,
44464                 clickEvent:'mousedown',
44465                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44466                 tabIndex:-1
44467             };
44468         }
44469         // create a new element.
44470         var wdiv = editor.wrap.createChild({
44471                 tag: 'div'
44472             }, editor.wrap.dom.firstChild.nextSibling, true);
44473         
44474         // can we do this more than once??
44475         
44476          // stop form submits
44477       
44478  
44479         // disable everything...
44480         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44481         this.toolbars = {};
44482            
44483         for (var i in  ty) {
44484           
44485             this.toolbars[i] = this.buildToolbar(ty[i],i);
44486         }
44487         this.tb = this.toolbars.BODY;
44488         this.tb.el.show();
44489         this.buildFooter();
44490         this.footer.show();
44491         editor.on('hide', function( ) { this.footer.hide() }, this);
44492         editor.on('show', function( ) { this.footer.show() }, this);
44493         
44494          
44495         this.rendered = true;
44496         
44497         // the all the btns;
44498         editor.on('editorevent', this.updateToolbar, this);
44499         // other toolbars need to implement this..
44500         //editor.on('editmodechange', this.updateToolbar, this);
44501     },
44502     
44503     
44504     
44505     /**
44506      * Protected method that will not generally be called directly. It triggers
44507      * a toolbar update by reading the markup state of the current selection in the editor.
44508      *
44509      * Note you can force an update by calling on('editorevent', scope, false)
44510      */
44511     updateToolbar: function(editor,ev,sel){
44512
44513         //Roo.log(ev);
44514         // capture mouse up - this is handy for selecting images..
44515         // perhaps should go somewhere else...
44516         if(!this.editorcore.activated){
44517              this.editor.onFirstFocus();
44518             return;
44519         }
44520         
44521         
44522         
44523         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44524         // selectNode - might want to handle IE?
44525         if (ev &&
44526             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44527             ev.target && ev.target.tagName == 'IMG') {
44528             // they have click on an image...
44529             // let's see if we can change the selection...
44530             sel = ev.target;
44531          
44532               var nodeRange = sel.ownerDocument.createRange();
44533             try {
44534                 nodeRange.selectNode(sel);
44535             } catch (e) {
44536                 nodeRange.selectNodeContents(sel);
44537             }
44538             //nodeRange.collapse(true);
44539             var s = this.editorcore.win.getSelection();
44540             s.removeAllRanges();
44541             s.addRange(nodeRange);
44542         }  
44543         
44544       
44545         var updateFooter = sel ? false : true;
44546         
44547         
44548         var ans = this.editorcore.getAllAncestors();
44549         
44550         // pick
44551         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44552         
44553         if (!sel) { 
44554             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44555             sel = sel ? sel : this.editorcore.doc.body;
44556             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44557             
44558         }
44559         // pick a menu that exists..
44560         var tn = sel.tagName.toUpperCase();
44561         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44562         
44563         tn = sel.tagName.toUpperCase();
44564         
44565         var lastSel = this.tb.selectedNode;
44566         
44567         this.tb.selectedNode = sel;
44568         
44569         // if current menu does not match..
44570         
44571         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44572                 
44573             this.tb.el.hide();
44574             ///console.log("show: " + tn);
44575             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44576             this.tb.el.show();
44577             // update name
44578             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44579             
44580             
44581             // update attributes
44582             if (this.tb.fields) {
44583                 this.tb.fields.each(function(e) {
44584                     if (e.stylename) {
44585                         e.setValue(sel.style[e.stylename]);
44586                         return;
44587                     } 
44588                    e.setValue(sel.getAttribute(e.attrname));
44589                 });
44590             }
44591             
44592             var hasStyles = false;
44593             for(var i in this.styles) {
44594                 hasStyles = true;
44595                 break;
44596             }
44597             
44598             // update styles
44599             if (hasStyles) { 
44600                 var st = this.tb.fields.item(0);
44601                 
44602                 st.store.removeAll();
44603                
44604                 
44605                 var cn = sel.className.split(/\s+/);
44606                 
44607                 var avs = [];
44608                 if (this.styles['*']) {
44609                     
44610                     Roo.each(this.styles['*'], function(v) {
44611                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44612                     });
44613                 }
44614                 if (this.styles[tn]) { 
44615                     Roo.each(this.styles[tn], function(v) {
44616                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44617                     });
44618                 }
44619                 
44620                 st.store.loadData(avs);
44621                 st.collapse();
44622                 st.setValue(cn);
44623             }
44624             // flag our selected Node.
44625             this.tb.selectedNode = sel;
44626            
44627            
44628             Roo.menu.MenuMgr.hideAll();
44629
44630         }
44631         
44632         if (!updateFooter) {
44633             //this.footDisp.dom.innerHTML = ''; 
44634             return;
44635         }
44636         // update the footer
44637         //
44638         var html = '';
44639         
44640         this.footerEls = ans.reverse();
44641         Roo.each(this.footerEls, function(a,i) {
44642             if (!a) { return; }
44643             html += html.length ? ' &gt; '  :  '';
44644             
44645             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44646             
44647         });
44648        
44649         // 
44650         var sz = this.footDisp.up('td').getSize();
44651         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44652         this.footDisp.dom.style.marginLeft = '5px';
44653         
44654         this.footDisp.dom.style.overflow = 'hidden';
44655         
44656         this.footDisp.dom.innerHTML = html;
44657             
44658         //this.editorsyncValue();
44659     },
44660      
44661     
44662    
44663        
44664     // private
44665     onDestroy : function(){
44666         if(this.rendered){
44667             
44668             this.tb.items.each(function(item){
44669                 if(item.menu){
44670                     item.menu.removeAll();
44671                     if(item.menu.el){
44672                         item.menu.el.destroy();
44673                     }
44674                 }
44675                 item.destroy();
44676             });
44677              
44678         }
44679     },
44680     onFirstFocus: function() {
44681         // need to do this for all the toolbars..
44682         this.tb.items.each(function(item){
44683            item.enable();
44684         });
44685     },
44686     buildToolbar: function(tlist, nm)
44687     {
44688         var editor = this.editor;
44689         var editorcore = this.editorcore;
44690          // create a new element.
44691         var wdiv = editor.wrap.createChild({
44692                 tag: 'div'
44693             }, editor.wrap.dom.firstChild.nextSibling, true);
44694         
44695        
44696         var tb = new Roo.Toolbar(wdiv);
44697         // add the name..
44698         
44699         tb.add(nm+ ":&nbsp;");
44700         
44701         var styles = [];
44702         for(var i in this.styles) {
44703             styles.push(i);
44704         }
44705         
44706         // styles...
44707         if (styles && styles.length) {
44708             
44709             // this needs a multi-select checkbox...
44710             tb.addField( new Roo.form.ComboBox({
44711                 store: new Roo.data.SimpleStore({
44712                     id : 'val',
44713                     fields: ['val', 'selected'],
44714                     data : [] 
44715                 }),
44716                 name : '-roo-edit-className',
44717                 attrname : 'className',
44718                 displayField: 'val',
44719                 typeAhead: false,
44720                 mode: 'local',
44721                 editable : false,
44722                 triggerAction: 'all',
44723                 emptyText:'Select Style',
44724                 selectOnFocus:true,
44725                 width: 130,
44726                 listeners : {
44727                     'select': function(c, r, i) {
44728                         // initial support only for on class per el..
44729                         tb.selectedNode.className =  r ? r.get('val') : '';
44730                         editorcore.syncValue();
44731                     }
44732                 }
44733     
44734             }));
44735         }
44736         
44737         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44738         var tbops = tbc.options;
44739         
44740         for (var i in tlist) {
44741             
44742             var item = tlist[i];
44743             tb.add(item.title + ":&nbsp;");
44744             
44745             
44746             //optname == used so you can configure the options available..
44747             var opts = item.opts ? item.opts : false;
44748             if (item.optname) {
44749                 opts = tbops[item.optname];
44750            
44751             }
44752             
44753             if (opts) {
44754                 // opts == pulldown..
44755                 tb.addField( new Roo.form.ComboBox({
44756                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44757                         id : 'val',
44758                         fields: ['val', 'display'],
44759                         data : opts  
44760                     }),
44761                     name : '-roo-edit-' + i,
44762                     attrname : i,
44763                     stylename : item.style ? item.style : false,
44764                     displayField: item.displayField ? item.displayField : 'val',
44765                     valueField :  'val',
44766                     typeAhead: false,
44767                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44768                     editable : false,
44769                     triggerAction: 'all',
44770                     emptyText:'Select',
44771                     selectOnFocus:true,
44772                     width: item.width ? item.width  : 130,
44773                     listeners : {
44774                         'select': function(c, r, i) {
44775                             if (c.stylename) {
44776                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44777                                 return;
44778                             }
44779                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44780                         }
44781                     }
44782
44783                 }));
44784                 continue;
44785                     
44786                  
44787                 
44788                 tb.addField( new Roo.form.TextField({
44789                     name: i,
44790                     width: 100,
44791                     //allowBlank:false,
44792                     value: ''
44793                 }));
44794                 continue;
44795             }
44796             tb.addField( new Roo.form.TextField({
44797                 name: '-roo-edit-' + i,
44798                 attrname : i,
44799                 
44800                 width: item.width,
44801                 //allowBlank:true,
44802                 value: '',
44803                 listeners: {
44804                     'change' : function(f, nv, ov) {
44805                         tb.selectedNode.setAttribute(f.attrname, nv);
44806                     }
44807                 }
44808             }));
44809              
44810         }
44811         
44812         var _this = this;
44813         
44814         if(nm == 'BODY'){
44815             tb.addSeparator();
44816         
44817             tb.addButton( {
44818                 text: 'Stylesheets',
44819
44820                 listeners : {
44821                     click : function ()
44822                     {
44823                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44824                     }
44825                 }
44826             });
44827         }
44828         
44829         tb.addFill();
44830         tb.addButton( {
44831             text: 'Remove Tag',
44832     
44833             listeners : {
44834                 click : function ()
44835                 {
44836                     // remove
44837                     // undo does not work.
44838                      
44839                     var sn = tb.selectedNode;
44840                     
44841                     var pn = sn.parentNode;
44842                     
44843                     var stn =  sn.childNodes[0];
44844                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44845                     while (sn.childNodes.length) {
44846                         var node = sn.childNodes[0];
44847                         sn.removeChild(node);
44848                         //Roo.log(node);
44849                         pn.insertBefore(node, sn);
44850                         
44851                     }
44852                     pn.removeChild(sn);
44853                     var range = editorcore.createRange();
44854         
44855                     range.setStart(stn,0);
44856                     range.setEnd(en,0); //????
44857                     //range.selectNode(sel);
44858                     
44859                     
44860                     var selection = editorcore.getSelection();
44861                     selection.removeAllRanges();
44862                     selection.addRange(range);
44863                     
44864                     
44865                     
44866                     //_this.updateToolbar(null, null, pn);
44867                     _this.updateToolbar(null, null, null);
44868                     _this.footDisp.dom.innerHTML = ''; 
44869                 }
44870             }
44871             
44872                     
44873                 
44874             
44875         });
44876         
44877         
44878         tb.el.on('click', function(e){
44879             e.preventDefault(); // what does this do?
44880         });
44881         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44882         tb.el.hide();
44883         tb.name = nm;
44884         // dont need to disable them... as they will get hidden
44885         return tb;
44886          
44887         
44888     },
44889     buildFooter : function()
44890     {
44891         
44892         var fel = this.editor.wrap.createChild();
44893         this.footer = new Roo.Toolbar(fel);
44894         // toolbar has scrolly on left / right?
44895         var footDisp= new Roo.Toolbar.Fill();
44896         var _t = this;
44897         this.footer.add(
44898             {
44899                 text : '&lt;',
44900                 xtype: 'Button',
44901                 handler : function() {
44902                     _t.footDisp.scrollTo('left',0,true)
44903                 }
44904             }
44905         );
44906         this.footer.add( footDisp );
44907         this.footer.add( 
44908             {
44909                 text : '&gt;',
44910                 xtype: 'Button',
44911                 handler : function() {
44912                     // no animation..
44913                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44914                 }
44915             }
44916         );
44917         var fel = Roo.get(footDisp.el);
44918         fel.addClass('x-editor-context');
44919         this.footDispWrap = fel; 
44920         this.footDispWrap.overflow  = 'hidden';
44921         
44922         this.footDisp = fel.createChild();
44923         this.footDispWrap.on('click', this.onContextClick, this)
44924         
44925         
44926     },
44927     onContextClick : function (ev,dom)
44928     {
44929         ev.preventDefault();
44930         var  cn = dom.className;
44931         //Roo.log(cn);
44932         if (!cn.match(/x-ed-loc-/)) {
44933             return;
44934         }
44935         var n = cn.split('-').pop();
44936         var ans = this.footerEls;
44937         var sel = ans[n];
44938         
44939          // pick
44940         var range = this.editorcore.createRange();
44941         
44942         range.selectNodeContents(sel);
44943         //range.selectNode(sel);
44944         
44945         
44946         var selection = this.editorcore.getSelection();
44947         selection.removeAllRanges();
44948         selection.addRange(range);
44949         
44950         
44951         
44952         this.updateToolbar(null, null, sel);
44953         
44954         
44955     }
44956     
44957     
44958     
44959     
44960     
44961 });
44962
44963
44964
44965
44966
44967 /*
44968  * Based on:
44969  * Ext JS Library 1.1.1
44970  * Copyright(c) 2006-2007, Ext JS, LLC.
44971  *
44972  * Originally Released Under LGPL - original licence link has changed is not relivant.
44973  *
44974  * Fork - LGPL
44975  * <script type="text/javascript">
44976  */
44977  
44978 /**
44979  * @class Roo.form.BasicForm
44980  * @extends Roo.util.Observable
44981  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44982  * @constructor
44983  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44984  * @param {Object} config Configuration options
44985  */
44986 Roo.form.BasicForm = function(el, config){
44987     this.allItems = [];
44988     this.childForms = [];
44989     Roo.apply(this, config);
44990     /*
44991      * The Roo.form.Field items in this form.
44992      * @type MixedCollection
44993      */
44994      
44995      
44996     this.items = new Roo.util.MixedCollection(false, function(o){
44997         return o.id || (o.id = Roo.id());
44998     });
44999     this.addEvents({
45000         /**
45001          * @event beforeaction
45002          * Fires before any action is performed. Return false to cancel the action.
45003          * @param {Form} this
45004          * @param {Action} action The action to be performed
45005          */
45006         beforeaction: true,
45007         /**
45008          * @event actionfailed
45009          * Fires when an action fails.
45010          * @param {Form} this
45011          * @param {Action} action The action that failed
45012          */
45013         actionfailed : true,
45014         /**
45015          * @event actioncomplete
45016          * Fires when an action is completed.
45017          * @param {Form} this
45018          * @param {Action} action The action that completed
45019          */
45020         actioncomplete : true
45021     });
45022     if(el){
45023         this.initEl(el);
45024     }
45025     Roo.form.BasicForm.superclass.constructor.call(this);
45026 };
45027
45028 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
45029     /**
45030      * @cfg {String} method
45031      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
45032      */
45033     /**
45034      * @cfg {DataReader} reader
45035      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
45036      * This is optional as there is built-in support for processing JSON.
45037      */
45038     /**
45039      * @cfg {DataReader} errorReader
45040      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45041      * This is completely optional as there is built-in support for processing JSON.
45042      */
45043     /**
45044      * @cfg {String} url
45045      * The URL to use for form actions if one isn't supplied in the action options.
45046      */
45047     /**
45048      * @cfg {Boolean} fileUpload
45049      * Set to true if this form is a file upload.
45050      */
45051      
45052     /**
45053      * @cfg {Object} baseParams
45054      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45055      */
45056      /**
45057      
45058     /**
45059      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45060      */
45061     timeout: 30,
45062
45063     // private
45064     activeAction : null,
45065
45066     /**
45067      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45068      * or setValues() data instead of when the form was first created.
45069      */
45070     trackResetOnLoad : false,
45071     
45072     
45073     /**
45074      * childForms - used for multi-tab forms
45075      * @type {Array}
45076      */
45077     childForms : false,
45078     
45079     /**
45080      * allItems - full list of fields.
45081      * @type {Array}
45082      */
45083     allItems : false,
45084     
45085     /**
45086      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45087      * element by passing it or its id or mask the form itself by passing in true.
45088      * @type Mixed
45089      */
45090     waitMsgTarget : false,
45091
45092     // private
45093     initEl : function(el){
45094         this.el = Roo.get(el);
45095         this.id = this.el.id || Roo.id();
45096         this.el.on('submit', this.onSubmit, this);
45097         this.el.addClass('x-form');
45098     },
45099
45100     // private
45101     onSubmit : function(e){
45102         e.stopEvent();
45103     },
45104
45105     /**
45106      * Returns true if client-side validation on the form is successful.
45107      * @return Boolean
45108      */
45109     isValid : function(){
45110         var valid = true;
45111         this.items.each(function(f){
45112            if(!f.validate()){
45113                valid = false;
45114            }
45115         });
45116         return valid;
45117     },
45118
45119     /**
45120      * Returns true if any fields in this form have changed since their original load.
45121      * @return Boolean
45122      */
45123     isDirty : function(){
45124         var dirty = false;
45125         this.items.each(function(f){
45126            if(f.isDirty()){
45127                dirty = true;
45128                return false;
45129            }
45130         });
45131         return dirty;
45132     },
45133
45134     /**
45135      * Performs a predefined action (submit or load) or custom actions you define on this form.
45136      * @param {String} actionName The name of the action type
45137      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45138      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45139      * accept other config options):
45140      * <pre>
45141 Property          Type             Description
45142 ----------------  ---------------  ----------------------------------------------------------------------------------
45143 url               String           The url for the action (defaults to the form's url)
45144 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45145 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45146 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45147                                    validate the form on the client (defaults to false)
45148      * </pre>
45149      * @return {BasicForm} this
45150      */
45151     doAction : function(action, options){
45152         if(typeof action == 'string'){
45153             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45154         }
45155         if(this.fireEvent('beforeaction', this, action) !== false){
45156             this.beforeAction(action);
45157             action.run.defer(100, action);
45158         }
45159         return this;
45160     },
45161
45162     /**
45163      * Shortcut to do a submit action.
45164      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45165      * @return {BasicForm} this
45166      */
45167     submit : function(options){
45168         this.doAction('submit', options);
45169         return this;
45170     },
45171
45172     /**
45173      * Shortcut to do a load action.
45174      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45175      * @return {BasicForm} this
45176      */
45177     load : function(options){
45178         this.doAction('load', options);
45179         return this;
45180     },
45181
45182     /**
45183      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45184      * @param {Record} record The record to edit
45185      * @return {BasicForm} this
45186      */
45187     updateRecord : function(record){
45188         record.beginEdit();
45189         var fs = record.fields;
45190         fs.each(function(f){
45191             var field = this.findField(f.name);
45192             if(field){
45193                 record.set(f.name, field.getValue());
45194             }
45195         }, this);
45196         record.endEdit();
45197         return this;
45198     },
45199
45200     /**
45201      * Loads an Roo.data.Record into this form.
45202      * @param {Record} record The record to load
45203      * @return {BasicForm} this
45204      */
45205     loadRecord : function(record){
45206         this.setValues(record.data);
45207         return this;
45208     },
45209
45210     // private
45211     beforeAction : function(action){
45212         var o = action.options;
45213         
45214        
45215         if(this.waitMsgTarget === true){
45216             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45217         }else if(this.waitMsgTarget){
45218             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45219             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45220         }else {
45221             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45222         }
45223          
45224     },
45225
45226     // private
45227     afterAction : function(action, success){
45228         this.activeAction = null;
45229         var o = action.options;
45230         
45231         if(this.waitMsgTarget === true){
45232             this.el.unmask();
45233         }else if(this.waitMsgTarget){
45234             this.waitMsgTarget.unmask();
45235         }else{
45236             Roo.MessageBox.updateProgress(1);
45237             Roo.MessageBox.hide();
45238         }
45239          
45240         if(success){
45241             if(o.reset){
45242                 this.reset();
45243             }
45244             Roo.callback(o.success, o.scope, [this, action]);
45245             this.fireEvent('actioncomplete', this, action);
45246             
45247         }else{
45248             
45249             // failure condition..
45250             // we have a scenario where updates need confirming.
45251             // eg. if a locking scenario exists..
45252             // we look for { errors : { needs_confirm : true }} in the response.
45253             if (
45254                 (typeof(action.result) != 'undefined')  &&
45255                 (typeof(action.result.errors) != 'undefined')  &&
45256                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45257            ){
45258                 var _t = this;
45259                 Roo.MessageBox.confirm(
45260                     "Change requires confirmation",
45261                     action.result.errorMsg,
45262                     function(r) {
45263                         if (r != 'yes') {
45264                             return;
45265                         }
45266                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45267                     }
45268                     
45269                 );
45270                 
45271                 
45272                 
45273                 return;
45274             }
45275             
45276             Roo.callback(o.failure, o.scope, [this, action]);
45277             // show an error message if no failed handler is set..
45278             if (!this.hasListener('actionfailed')) {
45279                 Roo.MessageBox.alert("Error",
45280                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45281                         action.result.errorMsg :
45282                         "Saving Failed, please check your entries or try again"
45283                 );
45284             }
45285             
45286             this.fireEvent('actionfailed', this, action);
45287         }
45288         
45289     },
45290
45291     /**
45292      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45293      * @param {String} id The value to search for
45294      * @return Field
45295      */
45296     findField : function(id){
45297         var field = this.items.get(id);
45298         if(!field){
45299             this.items.each(function(f){
45300                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45301                     field = f;
45302                     return false;
45303                 }
45304             });
45305         }
45306         return field || null;
45307     },
45308
45309     /**
45310      * Add a secondary form to this one, 
45311      * Used to provide tabbed forms. One form is primary, with hidden values 
45312      * which mirror the elements from the other forms.
45313      * 
45314      * @param {Roo.form.Form} form to add.
45315      * 
45316      */
45317     addForm : function(form)
45318     {
45319        
45320         if (this.childForms.indexOf(form) > -1) {
45321             // already added..
45322             return;
45323         }
45324         this.childForms.push(form);
45325         var n = '';
45326         Roo.each(form.allItems, function (fe) {
45327             
45328             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45329             if (this.findField(n)) { // already added..
45330                 return;
45331             }
45332             var add = new Roo.form.Hidden({
45333                 name : n
45334             });
45335             add.render(this.el);
45336             
45337             this.add( add );
45338         }, this);
45339         
45340     },
45341     /**
45342      * Mark fields in this form invalid in bulk.
45343      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45344      * @return {BasicForm} this
45345      */
45346     markInvalid : function(errors){
45347         if(errors instanceof Array){
45348             for(var i = 0, len = errors.length; i < len; i++){
45349                 var fieldError = errors[i];
45350                 var f = this.findField(fieldError.id);
45351                 if(f){
45352                     f.markInvalid(fieldError.msg);
45353                 }
45354             }
45355         }else{
45356             var field, id;
45357             for(id in errors){
45358                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45359                     field.markInvalid(errors[id]);
45360                 }
45361             }
45362         }
45363         Roo.each(this.childForms || [], function (f) {
45364             f.markInvalid(errors);
45365         });
45366         
45367         return this;
45368     },
45369
45370     /**
45371      * Set values for fields in this form in bulk.
45372      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45373      * @return {BasicForm} this
45374      */
45375     setValues : function(values){
45376         if(values instanceof Array){ // array of objects
45377             for(var i = 0, len = values.length; i < len; i++){
45378                 var v = values[i];
45379                 var f = this.findField(v.id);
45380                 if(f){
45381                     f.setValue(v.value);
45382                     if(this.trackResetOnLoad){
45383                         f.originalValue = f.getValue();
45384                     }
45385                 }
45386             }
45387         }else{ // object hash
45388             var field, id;
45389             for(id in values){
45390                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45391                     
45392                     if (field.setFromData && 
45393                         field.valueField && 
45394                         field.displayField &&
45395                         // combos' with local stores can 
45396                         // be queried via setValue()
45397                         // to set their value..
45398                         (field.store && !field.store.isLocal)
45399                         ) {
45400                         // it's a combo
45401                         var sd = { };
45402                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45403                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45404                         field.setFromData(sd);
45405                         
45406                     } else {
45407                         field.setValue(values[id]);
45408                     }
45409                     
45410                     
45411                     if(this.trackResetOnLoad){
45412                         field.originalValue = field.getValue();
45413                     }
45414                 }
45415             }
45416         }
45417          
45418         Roo.each(this.childForms || [], function (f) {
45419             f.setValues(values);
45420         });
45421                 
45422         return this;
45423     },
45424
45425     /**
45426      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45427      * they are returned as an array.
45428      * @param {Boolean} asString
45429      * @return {Object}
45430      */
45431     getValues : function(asString){
45432         if (this.childForms) {
45433             // copy values from the child forms
45434             Roo.each(this.childForms, function (f) {
45435                 this.setValues(f.getValues());
45436             }, this);
45437         }
45438         
45439         
45440         
45441         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45442         if(asString === true){
45443             return fs;
45444         }
45445         return Roo.urlDecode(fs);
45446     },
45447     
45448     /**
45449      * Returns the fields in this form as an object with key/value pairs. 
45450      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45451      * @return {Object}
45452      */
45453     getFieldValues : function(with_hidden)
45454     {
45455         if (this.childForms) {
45456             // copy values from the child forms
45457             // should this call getFieldValues - probably not as we do not currently copy
45458             // hidden fields when we generate..
45459             Roo.each(this.childForms, function (f) {
45460                 this.setValues(f.getValues());
45461             }, this);
45462         }
45463         
45464         var ret = {};
45465         this.items.each(function(f){
45466             if (!f.getName()) {
45467                 return;
45468             }
45469             var v = f.getValue();
45470             if (f.inputType =='radio') {
45471                 if (typeof(ret[f.getName()]) == 'undefined') {
45472                     ret[f.getName()] = ''; // empty..
45473                 }
45474                 
45475                 if (!f.el.dom.checked) {
45476                     return;
45477                     
45478                 }
45479                 v = f.el.dom.value;
45480                 
45481             }
45482             
45483             // not sure if this supported any more..
45484             if ((typeof(v) == 'object') && f.getRawValue) {
45485                 v = f.getRawValue() ; // dates..
45486             }
45487             // combo boxes where name != hiddenName...
45488             if (f.name != f.getName()) {
45489                 ret[f.name] = f.getRawValue();
45490             }
45491             ret[f.getName()] = v;
45492         });
45493         
45494         return ret;
45495     },
45496
45497     /**
45498      * Clears all invalid messages in this form.
45499      * @return {BasicForm} this
45500      */
45501     clearInvalid : function(){
45502         this.items.each(function(f){
45503            f.clearInvalid();
45504         });
45505         
45506         Roo.each(this.childForms || [], function (f) {
45507             f.clearInvalid();
45508         });
45509         
45510         
45511         return this;
45512     },
45513
45514     /**
45515      * Resets this form.
45516      * @return {BasicForm} this
45517      */
45518     reset : function(){
45519         this.items.each(function(f){
45520             f.reset();
45521         });
45522         
45523         Roo.each(this.childForms || [], function (f) {
45524             f.reset();
45525         });
45526        
45527         
45528         return this;
45529     },
45530
45531     /**
45532      * Add Roo.form components to this form.
45533      * @param {Field} field1
45534      * @param {Field} field2 (optional)
45535      * @param {Field} etc (optional)
45536      * @return {BasicForm} this
45537      */
45538     add : function(){
45539         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45540         return this;
45541     },
45542
45543
45544     /**
45545      * Removes a field from the items collection (does NOT remove its markup).
45546      * @param {Field} field
45547      * @return {BasicForm} this
45548      */
45549     remove : function(field){
45550         this.items.remove(field);
45551         return this;
45552     },
45553
45554     /**
45555      * Looks at the fields in this form, checks them for an id attribute,
45556      * and calls applyTo on the existing dom element with that id.
45557      * @return {BasicForm} this
45558      */
45559     render : function(){
45560         this.items.each(function(f){
45561             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45562                 f.applyTo(f.id);
45563             }
45564         });
45565         return this;
45566     },
45567
45568     /**
45569      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45570      * @param {Object} values
45571      * @return {BasicForm} this
45572      */
45573     applyToFields : function(o){
45574         this.items.each(function(f){
45575            Roo.apply(f, o);
45576         });
45577         return this;
45578     },
45579
45580     /**
45581      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45582      * @param {Object} values
45583      * @return {BasicForm} this
45584      */
45585     applyIfToFields : function(o){
45586         this.items.each(function(f){
45587            Roo.applyIf(f, o);
45588         });
45589         return this;
45590     }
45591 });
45592
45593 // back compat
45594 Roo.BasicForm = Roo.form.BasicForm;/*
45595  * Based on:
45596  * Ext JS Library 1.1.1
45597  * Copyright(c) 2006-2007, Ext JS, LLC.
45598  *
45599  * Originally Released Under LGPL - original licence link has changed is not relivant.
45600  *
45601  * Fork - LGPL
45602  * <script type="text/javascript">
45603  */
45604
45605 /**
45606  * @class Roo.form.Form
45607  * @extends Roo.form.BasicForm
45608  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45609  * @constructor
45610  * @param {Object} config Configuration options
45611  */
45612 Roo.form.Form = function(config){
45613     var xitems =  [];
45614     if (config.items) {
45615         xitems = config.items;
45616         delete config.items;
45617     }
45618    
45619     
45620     Roo.form.Form.superclass.constructor.call(this, null, config);
45621     this.url = this.url || this.action;
45622     if(!this.root){
45623         this.root = new Roo.form.Layout(Roo.applyIf({
45624             id: Roo.id()
45625         }, config));
45626     }
45627     this.active = this.root;
45628     /**
45629      * Array of all the buttons that have been added to this form via {@link addButton}
45630      * @type Array
45631      */
45632     this.buttons = [];
45633     this.allItems = [];
45634     this.addEvents({
45635         /**
45636          * @event clientvalidation
45637          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45638          * @param {Form} this
45639          * @param {Boolean} valid true if the form has passed client-side validation
45640          */
45641         clientvalidation: true,
45642         /**
45643          * @event rendered
45644          * Fires when the form is rendered
45645          * @param {Roo.form.Form} form
45646          */
45647         rendered : true
45648     });
45649     
45650     if (this.progressUrl) {
45651             // push a hidden field onto the list of fields..
45652             this.addxtype( {
45653                     xns: Roo.form, 
45654                     xtype : 'Hidden', 
45655                     name : 'UPLOAD_IDENTIFIER' 
45656             });
45657         }
45658         
45659     
45660     Roo.each(xitems, this.addxtype, this);
45661     
45662     
45663     
45664 };
45665
45666 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45667     /**
45668      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45669      */
45670     /**
45671      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45672      */
45673     /**
45674      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45675      */
45676     buttonAlign:'center',
45677
45678     /**
45679      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45680      */
45681     minButtonWidth:75,
45682
45683     /**
45684      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45685      * This property cascades to child containers if not set.
45686      */
45687     labelAlign:'left',
45688
45689     /**
45690      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45691      * fires a looping event with that state. This is required to bind buttons to the valid
45692      * state using the config value formBind:true on the button.
45693      */
45694     monitorValid : false,
45695
45696     /**
45697      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45698      */
45699     monitorPoll : 200,
45700     
45701     /**
45702      * @cfg {String} progressUrl - Url to return progress data 
45703      */
45704     
45705     progressUrl : false,
45706   
45707     /**
45708      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45709      * fields are added and the column is closed. If no fields are passed the column remains open
45710      * until end() is called.
45711      * @param {Object} config The config to pass to the column
45712      * @param {Field} field1 (optional)
45713      * @param {Field} field2 (optional)
45714      * @param {Field} etc (optional)
45715      * @return Column The column container object
45716      */
45717     column : function(c){
45718         var col = new Roo.form.Column(c);
45719         this.start(col);
45720         if(arguments.length > 1){ // duplicate code required because of Opera
45721             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45722             this.end();
45723         }
45724         return col;
45725     },
45726
45727     /**
45728      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45729      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45730      * until end() is called.
45731      * @param {Object} config The config to pass to the fieldset
45732      * @param {Field} field1 (optional)
45733      * @param {Field} field2 (optional)
45734      * @param {Field} etc (optional)
45735      * @return FieldSet The fieldset container object
45736      */
45737     fieldset : function(c){
45738         var fs = new Roo.form.FieldSet(c);
45739         this.start(fs);
45740         if(arguments.length > 1){ // duplicate code required because of Opera
45741             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45742             this.end();
45743         }
45744         return fs;
45745     },
45746
45747     /**
45748      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45749      * fields are added and the container is closed. If no fields are passed the container remains open
45750      * until end() is called.
45751      * @param {Object} config The config to pass to the Layout
45752      * @param {Field} field1 (optional)
45753      * @param {Field} field2 (optional)
45754      * @param {Field} etc (optional)
45755      * @return Layout The container object
45756      */
45757     container : function(c){
45758         var l = new Roo.form.Layout(c);
45759         this.start(l);
45760         if(arguments.length > 1){ // duplicate code required because of Opera
45761             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45762             this.end();
45763         }
45764         return l;
45765     },
45766
45767     /**
45768      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45769      * @param {Object} container A Roo.form.Layout or subclass of Layout
45770      * @return {Form} this
45771      */
45772     start : function(c){
45773         // cascade label info
45774         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45775         this.active.stack.push(c);
45776         c.ownerCt = this.active;
45777         this.active = c;
45778         return this;
45779     },
45780
45781     /**
45782      * Closes the current open container
45783      * @return {Form} this
45784      */
45785     end : function(){
45786         if(this.active == this.root){
45787             return this;
45788         }
45789         this.active = this.active.ownerCt;
45790         return this;
45791     },
45792
45793     /**
45794      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45795      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45796      * as the label of the field.
45797      * @param {Field} field1
45798      * @param {Field} field2 (optional)
45799      * @param {Field} etc. (optional)
45800      * @return {Form} this
45801      */
45802     add : function(){
45803         this.active.stack.push.apply(this.active.stack, arguments);
45804         this.allItems.push.apply(this.allItems,arguments);
45805         var r = [];
45806         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45807             if(a[i].isFormField){
45808                 r.push(a[i]);
45809             }
45810         }
45811         if(r.length > 0){
45812             Roo.form.Form.superclass.add.apply(this, r);
45813         }
45814         return this;
45815     },
45816     
45817
45818     
45819     
45820     
45821      /**
45822      * Find any element that has been added to a form, using it's ID or name
45823      * This can include framesets, columns etc. along with regular fields..
45824      * @param {String} id - id or name to find.
45825      
45826      * @return {Element} e - or false if nothing found.
45827      */
45828     findbyId : function(id)
45829     {
45830         var ret = false;
45831         if (!id) {
45832             return ret;
45833         }
45834         Roo.each(this.allItems, function(f){
45835             if (f.id == id || f.name == id ){
45836                 ret = f;
45837                 return false;
45838             }
45839         });
45840         return ret;
45841     },
45842
45843     
45844     
45845     /**
45846      * Render this form into the passed container. This should only be called once!
45847      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45848      * @return {Form} this
45849      */
45850     render : function(ct)
45851     {
45852         
45853         
45854         
45855         ct = Roo.get(ct);
45856         var o = this.autoCreate || {
45857             tag: 'form',
45858             method : this.method || 'POST',
45859             id : this.id || Roo.id()
45860         };
45861         this.initEl(ct.createChild(o));
45862
45863         this.root.render(this.el);
45864         
45865        
45866              
45867         this.items.each(function(f){
45868             f.render('x-form-el-'+f.id);
45869         });
45870
45871         if(this.buttons.length > 0){
45872             // tables are required to maintain order and for correct IE layout
45873             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45874                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45875                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45876             }}, null, true);
45877             var tr = tb.getElementsByTagName('tr')[0];
45878             for(var i = 0, len = this.buttons.length; i < len; i++) {
45879                 var b = this.buttons[i];
45880                 var td = document.createElement('td');
45881                 td.className = 'x-form-btn-td';
45882                 b.render(tr.appendChild(td));
45883             }
45884         }
45885         if(this.monitorValid){ // initialize after render
45886             this.startMonitoring();
45887         }
45888         this.fireEvent('rendered', this);
45889         return this;
45890     },
45891
45892     /**
45893      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45894      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45895      * object or a valid Roo.DomHelper element config
45896      * @param {Function} handler The function called when the button is clicked
45897      * @param {Object} scope (optional) The scope of the handler function
45898      * @return {Roo.Button}
45899      */
45900     addButton : function(config, handler, scope){
45901         var bc = {
45902             handler: handler,
45903             scope: scope,
45904             minWidth: this.minButtonWidth,
45905             hideParent:true
45906         };
45907         if(typeof config == "string"){
45908             bc.text = config;
45909         }else{
45910             Roo.apply(bc, config);
45911         }
45912         var btn = new Roo.Button(null, bc);
45913         this.buttons.push(btn);
45914         return btn;
45915     },
45916
45917      /**
45918      * Adds a series of form elements (using the xtype property as the factory method.
45919      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45920      * @param {Object} config 
45921      */
45922     
45923     addxtype : function()
45924     {
45925         var ar = Array.prototype.slice.call(arguments, 0);
45926         var ret = false;
45927         for(var i = 0; i < ar.length; i++) {
45928             if (!ar[i]) {
45929                 continue; // skip -- if this happends something invalid got sent, we 
45930                 // should ignore it, as basically that interface element will not show up
45931                 // and that should be pretty obvious!!
45932             }
45933             
45934             if (Roo.form[ar[i].xtype]) {
45935                 ar[i].form = this;
45936                 var fe = Roo.factory(ar[i], Roo.form);
45937                 if (!ret) {
45938                     ret = fe;
45939                 }
45940                 fe.form = this;
45941                 if (fe.store) {
45942                     fe.store.form = this;
45943                 }
45944                 if (fe.isLayout) {  
45945                          
45946                     this.start(fe);
45947                     this.allItems.push(fe);
45948                     if (fe.items && fe.addxtype) {
45949                         fe.addxtype.apply(fe, fe.items);
45950                         delete fe.items;
45951                     }
45952                      this.end();
45953                     continue;
45954                 }
45955                 
45956                 
45957                  
45958                 this.add(fe);
45959               //  console.log('adding ' + ar[i].xtype);
45960             }
45961             if (ar[i].xtype == 'Button') {  
45962                 //console.log('adding button');
45963                 //console.log(ar[i]);
45964                 this.addButton(ar[i]);
45965                 this.allItems.push(fe);
45966                 continue;
45967             }
45968             
45969             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45970                 alert('end is not supported on xtype any more, use items');
45971             //    this.end();
45972             //    //console.log('adding end');
45973             }
45974             
45975         }
45976         return ret;
45977     },
45978     
45979     /**
45980      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45981      * option "monitorValid"
45982      */
45983     startMonitoring : function(){
45984         if(!this.bound){
45985             this.bound = true;
45986             Roo.TaskMgr.start({
45987                 run : this.bindHandler,
45988                 interval : this.monitorPoll || 200,
45989                 scope: this
45990             });
45991         }
45992     },
45993
45994     /**
45995      * Stops monitoring of the valid state of this form
45996      */
45997     stopMonitoring : function(){
45998         this.bound = false;
45999     },
46000
46001     // private
46002     bindHandler : function(){
46003         if(!this.bound){
46004             return false; // stops binding
46005         }
46006         var valid = true;
46007         this.items.each(function(f){
46008             if(!f.isValid(true)){
46009                 valid = false;
46010                 return false;
46011             }
46012         });
46013         for(var i = 0, len = this.buttons.length; i < len; i++){
46014             var btn = this.buttons[i];
46015             if(btn.formBind === true && btn.disabled === valid){
46016                 btn.setDisabled(!valid);
46017             }
46018         }
46019         this.fireEvent('clientvalidation', this, valid);
46020     }
46021     
46022     
46023     
46024     
46025     
46026     
46027     
46028     
46029 });
46030
46031
46032 // back compat
46033 Roo.Form = Roo.form.Form;
46034 /*
46035  * Based on:
46036  * Ext JS Library 1.1.1
46037  * Copyright(c) 2006-2007, Ext JS, LLC.
46038  *
46039  * Originally Released Under LGPL - original licence link has changed is not relivant.
46040  *
46041  * Fork - LGPL
46042  * <script type="text/javascript">
46043  */
46044
46045 // as we use this in bootstrap.
46046 Roo.namespace('Roo.form');
46047  /**
46048  * @class Roo.form.Action
46049  * Internal Class used to handle form actions
46050  * @constructor
46051  * @param {Roo.form.BasicForm} el The form element or its id
46052  * @param {Object} config Configuration options
46053  */
46054
46055  
46056  
46057 // define the action interface
46058 Roo.form.Action = function(form, options){
46059     this.form = form;
46060     this.options = options || {};
46061 };
46062 /**
46063  * Client Validation Failed
46064  * @const 
46065  */
46066 Roo.form.Action.CLIENT_INVALID = 'client';
46067 /**
46068  * Server Validation Failed
46069  * @const 
46070  */
46071 Roo.form.Action.SERVER_INVALID = 'server';
46072  /**
46073  * Connect to Server Failed
46074  * @const 
46075  */
46076 Roo.form.Action.CONNECT_FAILURE = 'connect';
46077 /**
46078  * Reading Data from Server Failed
46079  * @const 
46080  */
46081 Roo.form.Action.LOAD_FAILURE = 'load';
46082
46083 Roo.form.Action.prototype = {
46084     type : 'default',
46085     failureType : undefined,
46086     response : undefined,
46087     result : undefined,
46088
46089     // interface method
46090     run : function(options){
46091
46092     },
46093
46094     // interface method
46095     success : function(response){
46096
46097     },
46098
46099     // interface method
46100     handleResponse : function(response){
46101
46102     },
46103
46104     // default connection failure
46105     failure : function(response){
46106         
46107         this.response = response;
46108         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46109         this.form.afterAction(this, false);
46110     },
46111
46112     processResponse : function(response){
46113         this.response = response;
46114         if(!response.responseText){
46115             return true;
46116         }
46117         this.result = this.handleResponse(response);
46118         return this.result;
46119     },
46120
46121     // utility functions used internally
46122     getUrl : function(appendParams){
46123         var url = this.options.url || this.form.url || this.form.el.dom.action;
46124         if(appendParams){
46125             var p = this.getParams();
46126             if(p){
46127                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46128             }
46129         }
46130         return url;
46131     },
46132
46133     getMethod : function(){
46134         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46135     },
46136
46137     getParams : function(){
46138         var bp = this.form.baseParams;
46139         var p = this.options.params;
46140         if(p){
46141             if(typeof p == "object"){
46142                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46143             }else if(typeof p == 'string' && bp){
46144                 p += '&' + Roo.urlEncode(bp);
46145             }
46146         }else if(bp){
46147             p = Roo.urlEncode(bp);
46148         }
46149         return p;
46150     },
46151
46152     createCallback : function(){
46153         return {
46154             success: this.success,
46155             failure: this.failure,
46156             scope: this,
46157             timeout: (this.form.timeout*1000),
46158             upload: this.form.fileUpload ? this.success : undefined
46159         };
46160     }
46161 };
46162
46163 Roo.form.Action.Submit = function(form, options){
46164     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46165 };
46166
46167 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46168     type : 'submit',
46169
46170     haveProgress : false,
46171     uploadComplete : false,
46172     
46173     // uploadProgress indicator.
46174     uploadProgress : function()
46175     {
46176         if (!this.form.progressUrl) {
46177             return;
46178         }
46179         
46180         if (!this.haveProgress) {
46181             Roo.MessageBox.progress("Uploading", "Uploading");
46182         }
46183         if (this.uploadComplete) {
46184            Roo.MessageBox.hide();
46185            return;
46186         }
46187         
46188         this.haveProgress = true;
46189    
46190         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46191         
46192         var c = new Roo.data.Connection();
46193         c.request({
46194             url : this.form.progressUrl,
46195             params: {
46196                 id : uid
46197             },
46198             method: 'GET',
46199             success : function(req){
46200                //console.log(data);
46201                 var rdata = false;
46202                 var edata;
46203                 try  {
46204                    rdata = Roo.decode(req.responseText)
46205                 } catch (e) {
46206                     Roo.log("Invalid data from server..");
46207                     Roo.log(edata);
46208                     return;
46209                 }
46210                 if (!rdata || !rdata.success) {
46211                     Roo.log(rdata);
46212                     Roo.MessageBox.alert(Roo.encode(rdata));
46213                     return;
46214                 }
46215                 var data = rdata.data;
46216                 
46217                 if (this.uploadComplete) {
46218                    Roo.MessageBox.hide();
46219                    return;
46220                 }
46221                    
46222                 if (data){
46223                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46224                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46225                     );
46226                 }
46227                 this.uploadProgress.defer(2000,this);
46228             },
46229        
46230             failure: function(data) {
46231                 Roo.log('progress url failed ');
46232                 Roo.log(data);
46233             },
46234             scope : this
46235         });
46236            
46237     },
46238     
46239     
46240     run : function()
46241     {
46242         // run get Values on the form, so it syncs any secondary forms.
46243         this.form.getValues();
46244         
46245         var o = this.options;
46246         var method = this.getMethod();
46247         var isPost = method == 'POST';
46248         if(o.clientValidation === false || this.form.isValid()){
46249             
46250             if (this.form.progressUrl) {
46251                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46252                     (new Date() * 1) + '' + Math.random());
46253                     
46254             } 
46255             
46256             
46257             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46258                 form:this.form.el.dom,
46259                 url:this.getUrl(!isPost),
46260                 method: method,
46261                 params:isPost ? this.getParams() : null,
46262                 isUpload: this.form.fileUpload
46263             }));
46264             
46265             this.uploadProgress();
46266
46267         }else if (o.clientValidation !== false){ // client validation failed
46268             this.failureType = Roo.form.Action.CLIENT_INVALID;
46269             this.form.afterAction(this, false);
46270         }
46271     },
46272
46273     success : function(response)
46274     {
46275         this.uploadComplete= true;
46276         if (this.haveProgress) {
46277             Roo.MessageBox.hide();
46278         }
46279         
46280         
46281         var result = this.processResponse(response);
46282         if(result === true || result.success){
46283             this.form.afterAction(this, true);
46284             return;
46285         }
46286         if(result.errors){
46287             this.form.markInvalid(result.errors);
46288             this.failureType = Roo.form.Action.SERVER_INVALID;
46289         }
46290         this.form.afterAction(this, false);
46291     },
46292     failure : function(response)
46293     {
46294         this.uploadComplete= true;
46295         if (this.haveProgress) {
46296             Roo.MessageBox.hide();
46297         }
46298         
46299         this.response = response;
46300         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46301         this.form.afterAction(this, false);
46302     },
46303     
46304     handleResponse : function(response){
46305         if(this.form.errorReader){
46306             var rs = this.form.errorReader.read(response);
46307             var errors = [];
46308             if(rs.records){
46309                 for(var i = 0, len = rs.records.length; i < len; i++) {
46310                     var r = rs.records[i];
46311                     errors[i] = r.data;
46312                 }
46313             }
46314             if(errors.length < 1){
46315                 errors = null;
46316             }
46317             return {
46318                 success : rs.success,
46319                 errors : errors
46320             };
46321         }
46322         var ret = false;
46323         try {
46324             ret = Roo.decode(response.responseText);
46325         } catch (e) {
46326             ret = {
46327                 success: false,
46328                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46329                 errors : []
46330             };
46331         }
46332         return ret;
46333         
46334     }
46335 });
46336
46337
46338 Roo.form.Action.Load = function(form, options){
46339     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46340     this.reader = this.form.reader;
46341 };
46342
46343 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46344     type : 'load',
46345
46346     run : function(){
46347         
46348         Roo.Ajax.request(Roo.apply(
46349                 this.createCallback(), {
46350                     method:this.getMethod(),
46351                     url:this.getUrl(false),
46352                     params:this.getParams()
46353         }));
46354     },
46355
46356     success : function(response){
46357         
46358         var result = this.processResponse(response);
46359         if(result === true || !result.success || !result.data){
46360             this.failureType = Roo.form.Action.LOAD_FAILURE;
46361             this.form.afterAction(this, false);
46362             return;
46363         }
46364         this.form.clearInvalid();
46365         this.form.setValues(result.data);
46366         this.form.afterAction(this, true);
46367     },
46368
46369     handleResponse : function(response){
46370         if(this.form.reader){
46371             var rs = this.form.reader.read(response);
46372             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46373             return {
46374                 success : rs.success,
46375                 data : data
46376             };
46377         }
46378         return Roo.decode(response.responseText);
46379     }
46380 });
46381
46382 Roo.form.Action.ACTION_TYPES = {
46383     'load' : Roo.form.Action.Load,
46384     'submit' : Roo.form.Action.Submit
46385 };/*
46386  * Based on:
46387  * Ext JS Library 1.1.1
46388  * Copyright(c) 2006-2007, Ext JS, LLC.
46389  *
46390  * Originally Released Under LGPL - original licence link has changed is not relivant.
46391  *
46392  * Fork - LGPL
46393  * <script type="text/javascript">
46394  */
46395  
46396 /**
46397  * @class Roo.form.Layout
46398  * @extends Roo.Component
46399  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46400  * @constructor
46401  * @param {Object} config Configuration options
46402  */
46403 Roo.form.Layout = function(config){
46404     var xitems = [];
46405     if (config.items) {
46406         xitems = config.items;
46407         delete config.items;
46408     }
46409     Roo.form.Layout.superclass.constructor.call(this, config);
46410     this.stack = [];
46411     Roo.each(xitems, this.addxtype, this);
46412      
46413 };
46414
46415 Roo.extend(Roo.form.Layout, Roo.Component, {
46416     /**
46417      * @cfg {String/Object} autoCreate
46418      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46419      */
46420     /**
46421      * @cfg {String/Object/Function} style
46422      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46423      * a function which returns such a specification.
46424      */
46425     /**
46426      * @cfg {String} labelAlign
46427      * Valid values are "left," "top" and "right" (defaults to "left")
46428      */
46429     /**
46430      * @cfg {Number} labelWidth
46431      * Fixed width in pixels of all field labels (defaults to undefined)
46432      */
46433     /**
46434      * @cfg {Boolean} clear
46435      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46436      */
46437     clear : true,
46438     /**
46439      * @cfg {String} labelSeparator
46440      * The separator to use after field labels (defaults to ':')
46441      */
46442     labelSeparator : ':',
46443     /**
46444      * @cfg {Boolean} hideLabels
46445      * True to suppress the display of field labels in this layout (defaults to false)
46446      */
46447     hideLabels : false,
46448
46449     // private
46450     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46451     
46452     isLayout : true,
46453     
46454     // private
46455     onRender : function(ct, position){
46456         if(this.el){ // from markup
46457             this.el = Roo.get(this.el);
46458         }else {  // generate
46459             var cfg = this.getAutoCreate();
46460             this.el = ct.createChild(cfg, position);
46461         }
46462         if(this.style){
46463             this.el.applyStyles(this.style);
46464         }
46465         if(this.labelAlign){
46466             this.el.addClass('x-form-label-'+this.labelAlign);
46467         }
46468         if(this.hideLabels){
46469             this.labelStyle = "display:none";
46470             this.elementStyle = "padding-left:0;";
46471         }else{
46472             if(typeof this.labelWidth == 'number'){
46473                 this.labelStyle = "width:"+this.labelWidth+"px;";
46474                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46475             }
46476             if(this.labelAlign == 'top'){
46477                 this.labelStyle = "width:auto;";
46478                 this.elementStyle = "padding-left:0;";
46479             }
46480         }
46481         var stack = this.stack;
46482         var slen = stack.length;
46483         if(slen > 0){
46484             if(!this.fieldTpl){
46485                 var t = new Roo.Template(
46486                     '<div class="x-form-item {5}">',
46487                         '<label for="{0}" style="{2}">{1}{4}</label>',
46488                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46489                         '</div>',
46490                     '</div><div class="x-form-clear-left"></div>'
46491                 );
46492                 t.disableFormats = true;
46493                 t.compile();
46494                 Roo.form.Layout.prototype.fieldTpl = t;
46495             }
46496             for(var i = 0; i < slen; i++) {
46497                 if(stack[i].isFormField){
46498                     this.renderField(stack[i]);
46499                 }else{
46500                     this.renderComponent(stack[i]);
46501                 }
46502             }
46503         }
46504         if(this.clear){
46505             this.el.createChild({cls:'x-form-clear'});
46506         }
46507     },
46508
46509     // private
46510     renderField : function(f){
46511         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46512                f.id, //0
46513                f.fieldLabel, //1
46514                f.labelStyle||this.labelStyle||'', //2
46515                this.elementStyle||'', //3
46516                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46517                f.itemCls||this.itemCls||''  //5
46518        ], true).getPrevSibling());
46519     },
46520
46521     // private
46522     renderComponent : function(c){
46523         c.render(c.isLayout ? this.el : this.el.createChild());    
46524     },
46525     /**
46526      * Adds a object form elements (using the xtype property as the factory method.)
46527      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46528      * @param {Object} config 
46529      */
46530     addxtype : function(o)
46531     {
46532         // create the lement.
46533         o.form = this.form;
46534         var fe = Roo.factory(o, Roo.form);
46535         this.form.allItems.push(fe);
46536         this.stack.push(fe);
46537         
46538         if (fe.isFormField) {
46539             this.form.items.add(fe);
46540         }
46541          
46542         return fe;
46543     }
46544 });
46545
46546 /**
46547  * @class Roo.form.Column
46548  * @extends Roo.form.Layout
46549  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46550  * @constructor
46551  * @param {Object} config Configuration options
46552  */
46553 Roo.form.Column = function(config){
46554     Roo.form.Column.superclass.constructor.call(this, config);
46555 };
46556
46557 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46558     /**
46559      * @cfg {Number/String} width
46560      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46561      */
46562     /**
46563      * @cfg {String/Object} autoCreate
46564      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46565      */
46566
46567     // private
46568     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46569
46570     // private
46571     onRender : function(ct, position){
46572         Roo.form.Column.superclass.onRender.call(this, ct, position);
46573         if(this.width){
46574             this.el.setWidth(this.width);
46575         }
46576     }
46577 });
46578
46579
46580 /**
46581  * @class Roo.form.Row
46582  * @extends Roo.form.Layout
46583  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46584  * @constructor
46585  * @param {Object} config Configuration options
46586  */
46587
46588  
46589 Roo.form.Row = function(config){
46590     Roo.form.Row.superclass.constructor.call(this, config);
46591 };
46592  
46593 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46594       /**
46595      * @cfg {Number/String} width
46596      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46597      */
46598     /**
46599      * @cfg {Number/String} height
46600      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46601      */
46602     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46603     
46604     padWidth : 20,
46605     // private
46606     onRender : function(ct, position){
46607         //console.log('row render');
46608         if(!this.rowTpl){
46609             var t = new Roo.Template(
46610                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46611                     '<label for="{0}" style="{2}">{1}{4}</label>',
46612                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46613                     '</div>',
46614                 '</div>'
46615             );
46616             t.disableFormats = true;
46617             t.compile();
46618             Roo.form.Layout.prototype.rowTpl = t;
46619         }
46620         this.fieldTpl = this.rowTpl;
46621         
46622         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46623         var labelWidth = 100;
46624         
46625         if ((this.labelAlign != 'top')) {
46626             if (typeof this.labelWidth == 'number') {
46627                 labelWidth = this.labelWidth
46628             }
46629             this.padWidth =  20 + labelWidth;
46630             
46631         }
46632         
46633         Roo.form.Column.superclass.onRender.call(this, ct, position);
46634         if(this.width){
46635             this.el.setWidth(this.width);
46636         }
46637         if(this.height){
46638             this.el.setHeight(this.height);
46639         }
46640     },
46641     
46642     // private
46643     renderField : function(f){
46644         f.fieldEl = this.fieldTpl.append(this.el, [
46645                f.id, f.fieldLabel,
46646                f.labelStyle||this.labelStyle||'',
46647                this.elementStyle||'',
46648                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46649                f.itemCls||this.itemCls||'',
46650                f.width ? f.width + this.padWidth : 160 + this.padWidth
46651        ],true);
46652     }
46653 });
46654  
46655
46656 /**
46657  * @class Roo.form.FieldSet
46658  * @extends Roo.form.Layout
46659  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46660  * @constructor
46661  * @param {Object} config Configuration options
46662  */
46663 Roo.form.FieldSet = function(config){
46664     Roo.form.FieldSet.superclass.constructor.call(this, config);
46665 };
46666
46667 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46668     /**
46669      * @cfg {String} legend
46670      * The text to display as the legend for the FieldSet (defaults to '')
46671      */
46672     /**
46673      * @cfg {String/Object} autoCreate
46674      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46675      */
46676
46677     // private
46678     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46679
46680     // private
46681     onRender : function(ct, position){
46682         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46683         if(this.legend){
46684             this.setLegend(this.legend);
46685         }
46686     },
46687
46688     // private
46689     setLegend : function(text){
46690         if(this.rendered){
46691             this.el.child('legend').update(text);
46692         }
46693     }
46694 });/*
46695  * Based on:
46696  * Ext JS Library 1.1.1
46697  * Copyright(c) 2006-2007, Ext JS, LLC.
46698  *
46699  * Originally Released Under LGPL - original licence link has changed is not relivant.
46700  *
46701  * Fork - LGPL
46702  * <script type="text/javascript">
46703  */
46704 /**
46705  * @class Roo.form.VTypes
46706  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46707  * @singleton
46708  */
46709 Roo.form.VTypes = function(){
46710     // closure these in so they are only created once.
46711     var alpha = /^[a-zA-Z_]+$/;
46712     var alphanum = /^[a-zA-Z0-9_]+$/;
46713     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46714     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46715
46716     // All these messages and functions are configurable
46717     return {
46718         /**
46719          * The function used to validate email addresses
46720          * @param {String} value The email address
46721          */
46722         'email' : function(v){
46723             return email.test(v);
46724         },
46725         /**
46726          * The error text to display when the email validation function returns false
46727          * @type String
46728          */
46729         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46730         /**
46731          * The keystroke filter mask to be applied on email input
46732          * @type RegExp
46733          */
46734         'emailMask' : /[a-z0-9_\.\-@]/i,
46735
46736         /**
46737          * The function used to validate URLs
46738          * @param {String} value The URL
46739          */
46740         'url' : function(v){
46741             return url.test(v);
46742         },
46743         /**
46744          * The error text to display when the url validation function returns false
46745          * @type String
46746          */
46747         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46748         
46749         /**
46750          * The function used to validate alpha values
46751          * @param {String} value The value
46752          */
46753         'alpha' : function(v){
46754             return alpha.test(v);
46755         },
46756         /**
46757          * The error text to display when the alpha validation function returns false
46758          * @type String
46759          */
46760         'alphaText' : 'This field should only contain letters and _',
46761         /**
46762          * The keystroke filter mask to be applied on alpha input
46763          * @type RegExp
46764          */
46765         'alphaMask' : /[a-z_]/i,
46766
46767         /**
46768          * The function used to validate alphanumeric values
46769          * @param {String} value The value
46770          */
46771         'alphanum' : function(v){
46772             return alphanum.test(v);
46773         },
46774         /**
46775          * The error text to display when the alphanumeric validation function returns false
46776          * @type String
46777          */
46778         'alphanumText' : 'This field should only contain letters, numbers and _',
46779         /**
46780          * The keystroke filter mask to be applied on alphanumeric input
46781          * @type RegExp
46782          */
46783         'alphanumMask' : /[a-z0-9_]/i
46784     };
46785 }();//<script type="text/javascript">
46786
46787 /**
46788  * @class Roo.form.FCKeditor
46789  * @extends Roo.form.TextArea
46790  * Wrapper around the FCKEditor http://www.fckeditor.net
46791  * @constructor
46792  * Creates a new FCKeditor
46793  * @param {Object} config Configuration options
46794  */
46795 Roo.form.FCKeditor = function(config){
46796     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46797     this.addEvents({
46798          /**
46799          * @event editorinit
46800          * Fired when the editor is initialized - you can add extra handlers here..
46801          * @param {FCKeditor} this
46802          * @param {Object} the FCK object.
46803          */
46804         editorinit : true
46805     });
46806     
46807     
46808 };
46809 Roo.form.FCKeditor.editors = { };
46810 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46811 {
46812     //defaultAutoCreate : {
46813     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46814     //},
46815     // private
46816     /**
46817      * @cfg {Object} fck options - see fck manual for details.
46818      */
46819     fckconfig : false,
46820     
46821     /**
46822      * @cfg {Object} fck toolbar set (Basic or Default)
46823      */
46824     toolbarSet : 'Basic',
46825     /**
46826      * @cfg {Object} fck BasePath
46827      */ 
46828     basePath : '/fckeditor/',
46829     
46830     
46831     frame : false,
46832     
46833     value : '',
46834     
46835    
46836     onRender : function(ct, position)
46837     {
46838         if(!this.el){
46839             this.defaultAutoCreate = {
46840                 tag: "textarea",
46841                 style:"width:300px;height:60px;",
46842                 autocomplete: "new-password"
46843             };
46844         }
46845         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46846         /*
46847         if(this.grow){
46848             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46849             if(this.preventScrollbars){
46850                 this.el.setStyle("overflow", "hidden");
46851             }
46852             this.el.setHeight(this.growMin);
46853         }
46854         */
46855         //console.log('onrender' + this.getId() );
46856         Roo.form.FCKeditor.editors[this.getId()] = this;
46857          
46858
46859         this.replaceTextarea() ;
46860         
46861     },
46862     
46863     getEditor : function() {
46864         return this.fckEditor;
46865     },
46866     /**
46867      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46868      * @param {Mixed} value The value to set
46869      */
46870     
46871     
46872     setValue : function(value)
46873     {
46874         //console.log('setValue: ' + value);
46875         
46876         if(typeof(value) == 'undefined') { // not sure why this is happending...
46877             return;
46878         }
46879         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46880         
46881         //if(!this.el || !this.getEditor()) {
46882         //    this.value = value;
46883             //this.setValue.defer(100,this,[value]);    
46884         //    return;
46885         //} 
46886         
46887         if(!this.getEditor()) {
46888             return;
46889         }
46890         
46891         this.getEditor().SetData(value);
46892         
46893         //
46894
46895     },
46896
46897     /**
46898      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46899      * @return {Mixed} value The field value
46900      */
46901     getValue : function()
46902     {
46903         
46904         if (this.frame && this.frame.dom.style.display == 'none') {
46905             return Roo.form.FCKeditor.superclass.getValue.call(this);
46906         }
46907         
46908         if(!this.el || !this.getEditor()) {
46909            
46910            // this.getValue.defer(100,this); 
46911             return this.value;
46912         }
46913        
46914         
46915         var value=this.getEditor().GetData();
46916         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46917         return Roo.form.FCKeditor.superclass.getValue.call(this);
46918         
46919
46920     },
46921
46922     /**
46923      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46924      * @return {Mixed} value The field value
46925      */
46926     getRawValue : function()
46927     {
46928         if (this.frame && this.frame.dom.style.display == 'none') {
46929             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46930         }
46931         
46932         if(!this.el || !this.getEditor()) {
46933             //this.getRawValue.defer(100,this); 
46934             return this.value;
46935             return;
46936         }
46937         
46938         
46939         
46940         var value=this.getEditor().GetData();
46941         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46942         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46943          
46944     },
46945     
46946     setSize : function(w,h) {
46947         
46948         
46949         
46950         //if (this.frame && this.frame.dom.style.display == 'none') {
46951         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46952         //    return;
46953         //}
46954         //if(!this.el || !this.getEditor()) {
46955         //    this.setSize.defer(100,this, [w,h]); 
46956         //    return;
46957         //}
46958         
46959         
46960         
46961         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46962         
46963         this.frame.dom.setAttribute('width', w);
46964         this.frame.dom.setAttribute('height', h);
46965         this.frame.setSize(w,h);
46966         
46967     },
46968     
46969     toggleSourceEdit : function(value) {
46970         
46971       
46972          
46973         this.el.dom.style.display = value ? '' : 'none';
46974         this.frame.dom.style.display = value ?  'none' : '';
46975         
46976     },
46977     
46978     
46979     focus: function(tag)
46980     {
46981         if (this.frame.dom.style.display == 'none') {
46982             return Roo.form.FCKeditor.superclass.focus.call(this);
46983         }
46984         if(!this.el || !this.getEditor()) {
46985             this.focus.defer(100,this, [tag]); 
46986             return;
46987         }
46988         
46989         
46990         
46991         
46992         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46993         this.getEditor().Focus();
46994         if (tgs.length) {
46995             if (!this.getEditor().Selection.GetSelection()) {
46996                 this.focus.defer(100,this, [tag]); 
46997                 return;
46998             }
46999             
47000             
47001             var r = this.getEditor().EditorDocument.createRange();
47002             r.setStart(tgs[0],0);
47003             r.setEnd(tgs[0],0);
47004             this.getEditor().Selection.GetSelection().removeAllRanges();
47005             this.getEditor().Selection.GetSelection().addRange(r);
47006             this.getEditor().Focus();
47007         }
47008         
47009     },
47010     
47011     
47012     
47013     replaceTextarea : function()
47014     {
47015         if ( document.getElementById( this.getId() + '___Frame' ) )
47016             return ;
47017         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
47018         //{
47019             // We must check the elements firstly using the Id and then the name.
47020         var oTextarea = document.getElementById( this.getId() );
47021         
47022         var colElementsByName = document.getElementsByName( this.getId() ) ;
47023          
47024         oTextarea.style.display = 'none' ;
47025
47026         if ( oTextarea.tabIndex ) {            
47027             this.TabIndex = oTextarea.tabIndex ;
47028         }
47029         
47030         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
47031         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
47032         this.frame = Roo.get(this.getId() + '___Frame')
47033     },
47034     
47035     _getConfigHtml : function()
47036     {
47037         var sConfig = '' ;
47038
47039         for ( var o in this.fckconfig ) {
47040             sConfig += sConfig.length > 0  ? '&amp;' : '';
47041             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47042         }
47043
47044         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47045     },
47046     
47047     
47048     _getIFrameHtml : function()
47049     {
47050         var sFile = 'fckeditor.html' ;
47051         /* no idea what this is about..
47052         try
47053         {
47054             if ( (/fcksource=true/i).test( window.top.location.search ) )
47055                 sFile = 'fckeditor.original.html' ;
47056         }
47057         catch (e) { 
47058         */
47059
47060         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47061         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47062         
47063         
47064         var html = '<iframe id="' + this.getId() +
47065             '___Frame" src="' + sLink +
47066             '" width="' + this.width +
47067             '" height="' + this.height + '"' +
47068             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47069             ' frameborder="0" scrolling="no"></iframe>' ;
47070
47071         return html ;
47072     },
47073     
47074     _insertHtmlBefore : function( html, element )
47075     {
47076         if ( element.insertAdjacentHTML )       {
47077             // IE
47078             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47079         } else { // Gecko
47080             var oRange = document.createRange() ;
47081             oRange.setStartBefore( element ) ;
47082             var oFragment = oRange.createContextualFragment( html );
47083             element.parentNode.insertBefore( oFragment, element ) ;
47084         }
47085     }
47086     
47087     
47088   
47089     
47090     
47091     
47092     
47093
47094 });
47095
47096 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47097
47098 function FCKeditor_OnComplete(editorInstance){
47099     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47100     f.fckEditor = editorInstance;
47101     //console.log("loaded");
47102     f.fireEvent('editorinit', f, editorInstance);
47103
47104   
47105
47106  
47107
47108
47109
47110
47111
47112
47113
47114
47115
47116
47117
47118
47119
47120
47121
47122 //<script type="text/javascript">
47123 /**
47124  * @class Roo.form.GridField
47125  * @extends Roo.form.Field
47126  * Embed a grid (or editable grid into a form)
47127  * STATUS ALPHA
47128  * 
47129  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47130  * it needs 
47131  * xgrid.store = Roo.data.Store
47132  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47133  * xgrid.store.reader = Roo.data.JsonReader 
47134  * 
47135  * 
47136  * @constructor
47137  * Creates a new GridField
47138  * @param {Object} config Configuration options
47139  */
47140 Roo.form.GridField = function(config){
47141     Roo.form.GridField.superclass.constructor.call(this, config);
47142      
47143 };
47144
47145 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47146     /**
47147      * @cfg {Number} width  - used to restrict width of grid..
47148      */
47149     width : 100,
47150     /**
47151      * @cfg {Number} height - used to restrict height of grid..
47152      */
47153     height : 50,
47154      /**
47155      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47156          * 
47157          *}
47158      */
47159     xgrid : false, 
47160     /**
47161      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47162      * {tag: "input", type: "checkbox", autocomplete: "off"})
47163      */
47164    // defaultAutoCreate : { tag: 'div' },
47165     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47166     /**
47167      * @cfg {String} addTitle Text to include for adding a title.
47168      */
47169     addTitle : false,
47170     //
47171     onResize : function(){
47172         Roo.form.Field.superclass.onResize.apply(this, arguments);
47173     },
47174
47175     initEvents : function(){
47176         // Roo.form.Checkbox.superclass.initEvents.call(this);
47177         // has no events...
47178        
47179     },
47180
47181
47182     getResizeEl : function(){
47183         return this.wrap;
47184     },
47185
47186     getPositionEl : function(){
47187         return this.wrap;
47188     },
47189
47190     // private
47191     onRender : function(ct, position){
47192         
47193         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47194         var style = this.style;
47195         delete this.style;
47196         
47197         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47198         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47199         this.viewEl = this.wrap.createChild({ tag: 'div' });
47200         if (style) {
47201             this.viewEl.applyStyles(style);
47202         }
47203         if (this.width) {
47204             this.viewEl.setWidth(this.width);
47205         }
47206         if (this.height) {
47207             this.viewEl.setHeight(this.height);
47208         }
47209         //if(this.inputValue !== undefined){
47210         //this.setValue(this.value);
47211         
47212         
47213         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47214         
47215         
47216         this.grid.render();
47217         this.grid.getDataSource().on('remove', this.refreshValue, this);
47218         this.grid.getDataSource().on('update', this.refreshValue, this);
47219         this.grid.on('afteredit', this.refreshValue, this);
47220  
47221     },
47222      
47223     
47224     /**
47225      * Sets the value of the item. 
47226      * @param {String} either an object  or a string..
47227      */
47228     setValue : function(v){
47229         //this.value = v;
47230         v = v || []; // empty set..
47231         // this does not seem smart - it really only affects memoryproxy grids..
47232         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47233             var ds = this.grid.getDataSource();
47234             // assumes a json reader..
47235             var data = {}
47236             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47237             ds.loadData( data);
47238         }
47239         // clear selection so it does not get stale.
47240         if (this.grid.sm) { 
47241             this.grid.sm.clearSelections();
47242         }
47243         
47244         Roo.form.GridField.superclass.setValue.call(this, v);
47245         this.refreshValue();
47246         // should load data in the grid really....
47247     },
47248     
47249     // private
47250     refreshValue: function() {
47251          var val = [];
47252         this.grid.getDataSource().each(function(r) {
47253             val.push(r.data);
47254         });
47255         this.el.dom.value = Roo.encode(val);
47256     }
47257     
47258      
47259     
47260     
47261 });/*
47262  * Based on:
47263  * Ext JS Library 1.1.1
47264  * Copyright(c) 2006-2007, Ext JS, LLC.
47265  *
47266  * Originally Released Under LGPL - original licence link has changed is not relivant.
47267  *
47268  * Fork - LGPL
47269  * <script type="text/javascript">
47270  */
47271 /**
47272  * @class Roo.form.DisplayField
47273  * @extends Roo.form.Field
47274  * A generic Field to display non-editable data.
47275  * @constructor
47276  * Creates a new Display Field item.
47277  * @param {Object} config Configuration options
47278  */
47279 Roo.form.DisplayField = function(config){
47280     Roo.form.DisplayField.superclass.constructor.call(this, config);
47281     
47282 };
47283
47284 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47285     inputType:      'hidden',
47286     allowBlank:     true,
47287     readOnly:         true,
47288     
47289  
47290     /**
47291      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47292      */
47293     focusClass : undefined,
47294     /**
47295      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47296      */
47297     fieldClass: 'x-form-field',
47298     
47299      /**
47300      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47301      */
47302     valueRenderer: undefined,
47303     
47304     width: 100,
47305     /**
47306      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47307      * {tag: "input", type: "checkbox", autocomplete: "off"})
47308      */
47309      
47310  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47311
47312     onResize : function(){
47313         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47314         
47315     },
47316
47317     initEvents : function(){
47318         // Roo.form.Checkbox.superclass.initEvents.call(this);
47319         // has no events...
47320        
47321     },
47322
47323
47324     getResizeEl : function(){
47325         return this.wrap;
47326     },
47327
47328     getPositionEl : function(){
47329         return this.wrap;
47330     },
47331
47332     // private
47333     onRender : function(ct, position){
47334         
47335         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47336         //if(this.inputValue !== undefined){
47337         this.wrap = this.el.wrap();
47338         
47339         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47340         
47341         if (this.bodyStyle) {
47342             this.viewEl.applyStyles(this.bodyStyle);
47343         }
47344         //this.viewEl.setStyle('padding', '2px');
47345         
47346         this.setValue(this.value);
47347         
47348     },
47349 /*
47350     // private
47351     initValue : Roo.emptyFn,
47352
47353   */
47354
47355         // private
47356     onClick : function(){
47357         
47358     },
47359
47360     /**
47361      * Sets the checked state of the checkbox.
47362      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47363      */
47364     setValue : function(v){
47365         this.value = v;
47366         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47367         // this might be called before we have a dom element..
47368         if (!this.viewEl) {
47369             return;
47370         }
47371         this.viewEl.dom.innerHTML = html;
47372         Roo.form.DisplayField.superclass.setValue.call(this, v);
47373
47374     }
47375 });/*
47376  * 
47377  * Licence- LGPL
47378  * 
47379  */
47380
47381 /**
47382  * @class Roo.form.DayPicker
47383  * @extends Roo.form.Field
47384  * A Day picker show [M] [T] [W] ....
47385  * @constructor
47386  * Creates a new Day Picker
47387  * @param {Object} config Configuration options
47388  */
47389 Roo.form.DayPicker= function(config){
47390     Roo.form.DayPicker.superclass.constructor.call(this, config);
47391      
47392 };
47393
47394 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47395     /**
47396      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47397      */
47398     focusClass : undefined,
47399     /**
47400      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47401      */
47402     fieldClass: "x-form-field",
47403    
47404     /**
47405      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47406      * {tag: "input", type: "checkbox", autocomplete: "off"})
47407      */
47408     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47409     
47410    
47411     actionMode : 'viewEl', 
47412     //
47413     // private
47414  
47415     inputType : 'hidden',
47416     
47417      
47418     inputElement: false, // real input element?
47419     basedOn: false, // ????
47420     
47421     isFormField: true, // not sure where this is needed!!!!
47422
47423     onResize : function(){
47424         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47425         if(!this.boxLabel){
47426             this.el.alignTo(this.wrap, 'c-c');
47427         }
47428     },
47429
47430     initEvents : function(){
47431         Roo.form.Checkbox.superclass.initEvents.call(this);
47432         this.el.on("click", this.onClick,  this);
47433         this.el.on("change", this.onClick,  this);
47434     },
47435
47436
47437     getResizeEl : function(){
47438         return this.wrap;
47439     },
47440
47441     getPositionEl : function(){
47442         return this.wrap;
47443     },
47444
47445     
47446     // private
47447     onRender : function(ct, position){
47448         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47449        
47450         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47451         
47452         var r1 = '<table><tr>';
47453         var r2 = '<tr class="x-form-daypick-icons">';
47454         for (var i=0; i < 7; i++) {
47455             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47456             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47457         }
47458         
47459         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47460         viewEl.select('img').on('click', this.onClick, this);
47461         this.viewEl = viewEl;   
47462         
47463         
47464         // this will not work on Chrome!!!
47465         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47466         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47467         
47468         
47469           
47470
47471     },
47472
47473     // private
47474     initValue : Roo.emptyFn,
47475
47476     /**
47477      * Returns the checked state of the checkbox.
47478      * @return {Boolean} True if checked, else false
47479      */
47480     getValue : function(){
47481         return this.el.dom.value;
47482         
47483     },
47484
47485         // private
47486     onClick : function(e){ 
47487         //this.setChecked(!this.checked);
47488         Roo.get(e.target).toggleClass('x-menu-item-checked');
47489         this.refreshValue();
47490         //if(this.el.dom.checked != this.checked){
47491         //    this.setValue(this.el.dom.checked);
47492        // }
47493     },
47494     
47495     // private
47496     refreshValue : function()
47497     {
47498         var val = '';
47499         this.viewEl.select('img',true).each(function(e,i,n)  {
47500             val += e.is(".x-menu-item-checked") ? String(n) : '';
47501         });
47502         this.setValue(val, true);
47503     },
47504
47505     /**
47506      * Sets the checked state of the checkbox.
47507      * On is always based on a string comparison between inputValue and the param.
47508      * @param {Boolean/String} value - the value to set 
47509      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47510      */
47511     setValue : function(v,suppressEvent){
47512         if (!this.el.dom) {
47513             return;
47514         }
47515         var old = this.el.dom.value ;
47516         this.el.dom.value = v;
47517         if (suppressEvent) {
47518             return ;
47519         }
47520          
47521         // update display..
47522         this.viewEl.select('img',true).each(function(e,i,n)  {
47523             
47524             var on = e.is(".x-menu-item-checked");
47525             var newv = v.indexOf(String(n)) > -1;
47526             if (on != newv) {
47527                 e.toggleClass('x-menu-item-checked');
47528             }
47529             
47530         });
47531         
47532         
47533         this.fireEvent('change', this, v, old);
47534         
47535         
47536     },
47537    
47538     // handle setting of hidden value by some other method!!?!?
47539     setFromHidden: function()
47540     {
47541         if(!this.el){
47542             return;
47543         }
47544         //console.log("SET FROM HIDDEN");
47545         //alert('setFrom hidden');
47546         this.setValue(this.el.dom.value);
47547     },
47548     
47549     onDestroy : function()
47550     {
47551         if(this.viewEl){
47552             Roo.get(this.viewEl).remove();
47553         }
47554          
47555         Roo.form.DayPicker.superclass.onDestroy.call(this);
47556     }
47557
47558 });/*
47559  * RooJS Library 1.1.1
47560  * Copyright(c) 2008-2011  Alan Knowles
47561  *
47562  * License - LGPL
47563  */
47564  
47565
47566 /**
47567  * @class Roo.form.ComboCheck
47568  * @extends Roo.form.ComboBox
47569  * A combobox for multiple select items.
47570  *
47571  * FIXME - could do with a reset button..
47572  * 
47573  * @constructor
47574  * Create a new ComboCheck
47575  * @param {Object} config Configuration options
47576  */
47577 Roo.form.ComboCheck = function(config){
47578     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47579     // should verify some data...
47580     // like
47581     // hiddenName = required..
47582     // displayField = required
47583     // valudField == required
47584     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47585     var _t = this;
47586     Roo.each(req, function(e) {
47587         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47588             throw "Roo.form.ComboCheck : missing value for: " + e;
47589         }
47590     });
47591     
47592     
47593 };
47594
47595 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47596      
47597      
47598     editable : false,
47599      
47600     selectedClass: 'x-menu-item-checked', 
47601     
47602     // private
47603     onRender : function(ct, position){
47604         var _t = this;
47605         
47606         
47607         
47608         if(!this.tpl){
47609             var cls = 'x-combo-list';
47610
47611             
47612             this.tpl =  new Roo.Template({
47613                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47614                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47615                    '<span>{' + this.displayField + '}</span>' +
47616                     '</div>' 
47617                 
47618             });
47619         }
47620  
47621         
47622         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47623         this.view.singleSelect = false;
47624         this.view.multiSelect = true;
47625         this.view.toggleSelect = true;
47626         this.pageTb.add(new Roo.Toolbar.Fill(), {
47627             
47628             text: 'Done',
47629             handler: function()
47630             {
47631                 _t.collapse();
47632             }
47633         });
47634     },
47635     
47636     onViewOver : function(e, t){
47637         // do nothing...
47638         return;
47639         
47640     },
47641     
47642     onViewClick : function(doFocus,index){
47643         return;
47644         
47645     },
47646     select: function () {
47647         //Roo.log("SELECT CALLED");
47648     },
47649      
47650     selectByValue : function(xv, scrollIntoView){
47651         var ar = this.getValueArray();
47652         var sels = [];
47653         
47654         Roo.each(ar, function(v) {
47655             if(v === undefined || v === null){
47656                 return;
47657             }
47658             var r = this.findRecord(this.valueField, v);
47659             if(r){
47660                 sels.push(this.store.indexOf(r))
47661                 
47662             }
47663         },this);
47664         this.view.select(sels);
47665         return false;
47666     },
47667     
47668     
47669     
47670     onSelect : function(record, index){
47671        // Roo.log("onselect Called");
47672        // this is only called by the clear button now..
47673         this.view.clearSelections();
47674         this.setValue('[]');
47675         if (this.value != this.valueBefore) {
47676             this.fireEvent('change', this, this.value, this.valueBefore);
47677             this.valueBefore = this.value;
47678         }
47679     },
47680     getValueArray : function()
47681     {
47682         var ar = [] ;
47683         
47684         try {
47685             //Roo.log(this.value);
47686             if (typeof(this.value) == 'undefined') {
47687                 return [];
47688             }
47689             var ar = Roo.decode(this.value);
47690             return  ar instanceof Array ? ar : []; //?? valid?
47691             
47692         } catch(e) {
47693             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47694             return [];
47695         }
47696          
47697     },
47698     expand : function ()
47699     {
47700         
47701         Roo.form.ComboCheck.superclass.expand.call(this);
47702         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47703         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47704         
47705
47706     },
47707     
47708     collapse : function(){
47709         Roo.form.ComboCheck.superclass.collapse.call(this);
47710         var sl = this.view.getSelectedIndexes();
47711         var st = this.store;
47712         var nv = [];
47713         var tv = [];
47714         var r;
47715         Roo.each(sl, function(i) {
47716             r = st.getAt(i);
47717             nv.push(r.get(this.valueField));
47718         },this);
47719         this.setValue(Roo.encode(nv));
47720         if (this.value != this.valueBefore) {
47721
47722             this.fireEvent('change', this, this.value, this.valueBefore);
47723             this.valueBefore = this.value;
47724         }
47725         
47726     },
47727     
47728     setValue : function(v){
47729         // Roo.log(v);
47730         this.value = v;
47731         
47732         var vals = this.getValueArray();
47733         var tv = [];
47734         Roo.each(vals, function(k) {
47735             var r = this.findRecord(this.valueField, k);
47736             if(r){
47737                 tv.push(r.data[this.displayField]);
47738             }else if(this.valueNotFoundText !== undefined){
47739                 tv.push( this.valueNotFoundText );
47740             }
47741         },this);
47742        // Roo.log(tv);
47743         
47744         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47745         this.hiddenField.value = v;
47746         this.value = v;
47747     }
47748     
47749 });/*
47750  * Based on:
47751  * Ext JS Library 1.1.1
47752  * Copyright(c) 2006-2007, Ext JS, LLC.
47753  *
47754  * Originally Released Under LGPL - original licence link has changed is not relivant.
47755  *
47756  * Fork - LGPL
47757  * <script type="text/javascript">
47758  */
47759  
47760 /**
47761  * @class Roo.form.Signature
47762  * @extends Roo.form.Field
47763  * Signature field.  
47764  * @constructor
47765  * 
47766  * @param {Object} config Configuration options
47767  */
47768
47769 Roo.form.Signature = function(config){
47770     Roo.form.Signature.superclass.constructor.call(this, config);
47771     
47772     this.addEvents({// not in used??
47773          /**
47774          * @event confirm
47775          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47776              * @param {Roo.form.Signature} combo This combo box
47777              */
47778         'confirm' : true,
47779         /**
47780          * @event reset
47781          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47782              * @param {Roo.form.ComboBox} combo This combo box
47783              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47784              */
47785         'reset' : true
47786     });
47787 };
47788
47789 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47790     /**
47791      * @cfg {Object} labels Label to use when rendering a form.
47792      * defaults to 
47793      * labels : { 
47794      *      clear : "Clear",
47795      *      confirm : "Confirm"
47796      *  }
47797      */
47798     labels : { 
47799         clear : "Clear",
47800         confirm : "Confirm"
47801     },
47802     /**
47803      * @cfg {Number} width The signature panel width (defaults to 300)
47804      */
47805     width: 300,
47806     /**
47807      * @cfg {Number} height The signature panel height (defaults to 100)
47808      */
47809     height : 100,
47810     /**
47811      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47812      */
47813     allowBlank : false,
47814     
47815     //private
47816     // {Object} signPanel The signature SVG panel element (defaults to {})
47817     signPanel : {},
47818     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47819     isMouseDown : false,
47820     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47821     isConfirmed : false,
47822     // {String} signatureTmp SVG mapping string (defaults to empty string)
47823     signatureTmp : '',
47824     
47825     
47826     defaultAutoCreate : { // modified by initCompnoent..
47827         tag: "input",
47828         type:"hidden"
47829     },
47830
47831     // private
47832     onRender : function(ct, position){
47833         
47834         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47835         
47836         this.wrap = this.el.wrap({
47837             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47838         });
47839         
47840         this.createToolbar(this);
47841         this.signPanel = this.wrap.createChild({
47842                 tag: 'div',
47843                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47844             }, this.el
47845         );
47846             
47847         this.svgID = Roo.id();
47848         this.svgEl = this.signPanel.createChild({
47849               xmlns : 'http://www.w3.org/2000/svg',
47850               tag : 'svg',
47851               id : this.svgID + "-svg",
47852               width: this.width,
47853               height: this.height,
47854               viewBox: '0 0 '+this.width+' '+this.height,
47855               cn : [
47856                 {
47857                     tag: "rect",
47858                     id: this.svgID + "-svg-r",
47859                     width: this.width,
47860                     height: this.height,
47861                     fill: "#ffa"
47862                 },
47863                 {
47864                     tag: "line",
47865                     id: this.svgID + "-svg-l",
47866                     x1: "0", // start
47867                     y1: (this.height*0.8), // start set the line in 80% of height
47868                     x2: this.width, // end
47869                     y2: (this.height*0.8), // end set the line in 80% of height
47870                     'stroke': "#666",
47871                     'stroke-width': "1",
47872                     'stroke-dasharray': "3",
47873                     'shape-rendering': "crispEdges",
47874                     'pointer-events': "none"
47875                 },
47876                 {
47877                     tag: "path",
47878                     id: this.svgID + "-svg-p",
47879                     'stroke': "navy",
47880                     'stroke-width': "3",
47881                     'fill': "none",
47882                     'pointer-events': 'none'
47883                 }
47884               ]
47885         });
47886         this.createSVG();
47887         this.svgBox = this.svgEl.dom.getScreenCTM();
47888     },
47889     createSVG : function(){ 
47890         var svg = this.signPanel;
47891         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47892         var t = this;
47893
47894         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47895         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47896         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47897         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47898         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47899         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47900         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47901         
47902     },
47903     isTouchEvent : function(e){
47904         return e.type.match(/^touch/);
47905     },
47906     getCoords : function (e) {
47907         var pt    = this.svgEl.dom.createSVGPoint();
47908         pt.x = e.clientX; 
47909         pt.y = e.clientY;
47910         if (this.isTouchEvent(e)) {
47911             pt.x =  e.targetTouches[0].clientX;
47912             pt.y = e.targetTouches[0].clientY;
47913         }
47914         var a = this.svgEl.dom.getScreenCTM();
47915         var b = a.inverse();
47916         var mx = pt.matrixTransform(b);
47917         return mx.x + ',' + mx.y;
47918     },
47919     //mouse event headler 
47920     down : function (e) {
47921         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47922         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47923         
47924         this.isMouseDown = true;
47925         
47926         e.preventDefault();
47927     },
47928     move : function (e) {
47929         if (this.isMouseDown) {
47930             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47931             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47932         }
47933         
47934         e.preventDefault();
47935     },
47936     up : function (e) {
47937         this.isMouseDown = false;
47938         var sp = this.signatureTmp.split(' ');
47939         
47940         if(sp.length > 1){
47941             if(!sp[sp.length-2].match(/^L/)){
47942                 sp.pop();
47943                 sp.pop();
47944                 sp.push("");
47945                 this.signatureTmp = sp.join(" ");
47946             }
47947         }
47948         if(this.getValue() != this.signatureTmp){
47949             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47950             this.isConfirmed = false;
47951         }
47952         e.preventDefault();
47953     },
47954     
47955     /**
47956      * Protected method that will not generally be called directly. It
47957      * is called when the editor creates its toolbar. Override this method if you need to
47958      * add custom toolbar buttons.
47959      * @param {HtmlEditor} editor
47960      */
47961     createToolbar : function(editor){
47962          function btn(id, toggle, handler){
47963             var xid = fid + '-'+ id ;
47964             return {
47965                 id : xid,
47966                 cmd : id,
47967                 cls : 'x-btn-icon x-edit-'+id,
47968                 enableToggle:toggle !== false,
47969                 scope: editor, // was editor...
47970                 handler:handler||editor.relayBtnCmd,
47971                 clickEvent:'mousedown',
47972                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47973                 tabIndex:-1
47974             };
47975         }
47976         
47977         
47978         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47979         this.tb = tb;
47980         this.tb.add(
47981            {
47982                 cls : ' x-signature-btn x-signature-'+id,
47983                 scope: editor, // was editor...
47984                 handler: this.reset,
47985                 clickEvent:'mousedown',
47986                 text: this.labels.clear
47987             },
47988             {
47989                  xtype : 'Fill',
47990                  xns: Roo.Toolbar
47991             }, 
47992             {
47993                 cls : '  x-signature-btn x-signature-'+id,
47994                 scope: editor, // was editor...
47995                 handler: this.confirmHandler,
47996                 clickEvent:'mousedown',
47997                 text: this.labels.confirm
47998             }
47999         );
48000     
48001     },
48002     //public
48003     /**
48004      * when user is clicked confirm then show this image.....
48005      * 
48006      * @return {String} Image Data URI
48007      */
48008     getImageDataURI : function(){
48009         var svg = this.svgEl.dom.parentNode.innerHTML;
48010         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
48011         return src; 
48012     },
48013     /**
48014      * 
48015      * @return {Boolean} this.isConfirmed
48016      */
48017     getConfirmed : function(){
48018         return this.isConfirmed;
48019     },
48020     /**
48021      * 
48022      * @return {Number} this.width
48023      */
48024     getWidth : function(){
48025         return this.width;
48026     },
48027     /**
48028      * 
48029      * @return {Number} this.height
48030      */
48031     getHeight : function(){
48032         return this.height;
48033     },
48034     // private
48035     getSignature : function(){
48036         return this.signatureTmp;
48037     },
48038     // private
48039     reset : function(){
48040         this.signatureTmp = '';
48041         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48042         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48043         this.isConfirmed = false;
48044         Roo.form.Signature.superclass.reset.call(this);
48045     },
48046     setSignature : function(s){
48047         this.signatureTmp = s;
48048         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48049         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48050         this.setValue(s);
48051         this.isConfirmed = false;
48052         Roo.form.Signature.superclass.reset.call(this);
48053     }, 
48054     test : function(){
48055 //        Roo.log(this.signPanel.dom.contentWindow.up())
48056     },
48057     //private
48058     setConfirmed : function(){
48059         
48060         
48061         
48062 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48063     },
48064     // private
48065     confirmHandler : function(){
48066         if(!this.getSignature()){
48067             return;
48068         }
48069         
48070         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48071         this.setValue(this.getSignature());
48072         this.isConfirmed = true;
48073         
48074         this.fireEvent('confirm', this);
48075     },
48076     // private
48077     // Subclasses should provide the validation implementation by overriding this
48078     validateValue : function(value){
48079         if(this.allowBlank){
48080             return true;
48081         }
48082         
48083         if(this.isConfirmed){
48084             return true;
48085         }
48086         return false;
48087     }
48088 });/*
48089  * Based on:
48090  * Ext JS Library 1.1.1
48091  * Copyright(c) 2006-2007, Ext JS, LLC.
48092  *
48093  * Originally Released Under LGPL - original licence link has changed is not relivant.
48094  *
48095  * Fork - LGPL
48096  * <script type="text/javascript">
48097  */
48098  
48099
48100 /**
48101  * @class Roo.form.ComboBox
48102  * @extends Roo.form.TriggerField
48103  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48104  * @constructor
48105  * Create a new ComboBox.
48106  * @param {Object} config Configuration options
48107  */
48108 Roo.form.Select = function(config){
48109     Roo.form.Select.superclass.constructor.call(this, config);
48110      
48111 };
48112
48113 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48114     /**
48115      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48116      */
48117     /**
48118      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48119      * rendering into an Roo.Editor, defaults to false)
48120      */
48121     /**
48122      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48123      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48124      */
48125     /**
48126      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48127      */
48128     /**
48129      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48130      * the dropdown list (defaults to undefined, with no header element)
48131      */
48132
48133      /**
48134      * @cfg {String/Roo.Template} tpl The template to use to render the output
48135      */
48136      
48137     // private
48138     defaultAutoCreate : {tag: "select"  },
48139     /**
48140      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48141      */
48142     listWidth: undefined,
48143     /**
48144      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48145      * mode = 'remote' or 'text' if mode = 'local')
48146      */
48147     displayField: undefined,
48148     /**
48149      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48150      * mode = 'remote' or 'value' if mode = 'local'). 
48151      * Note: use of a valueField requires the user make a selection
48152      * in order for a value to be mapped.
48153      */
48154     valueField: undefined,
48155     
48156     
48157     /**
48158      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48159      * field's data value (defaults to the underlying DOM element's name)
48160      */
48161     hiddenName: undefined,
48162     /**
48163      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48164      */
48165     listClass: '',
48166     /**
48167      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48168      */
48169     selectedClass: 'x-combo-selected',
48170     /**
48171      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48172      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48173      * which displays a downward arrow icon).
48174      */
48175     triggerClass : 'x-form-arrow-trigger',
48176     /**
48177      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48178      */
48179     shadow:'sides',
48180     /**
48181      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48182      * anchor positions (defaults to 'tl-bl')
48183      */
48184     listAlign: 'tl-bl?',
48185     /**
48186      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48187      */
48188     maxHeight: 300,
48189     /**
48190      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48191      * query specified by the allQuery config option (defaults to 'query')
48192      */
48193     triggerAction: 'query',
48194     /**
48195      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48196      * (defaults to 4, does not apply if editable = false)
48197      */
48198     minChars : 4,
48199     /**
48200      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48201      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48202      */
48203     typeAhead: false,
48204     /**
48205      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48206      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48207      */
48208     queryDelay: 500,
48209     /**
48210      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48211      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48212      */
48213     pageSize: 0,
48214     /**
48215      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48216      * when editable = true (defaults to false)
48217      */
48218     selectOnFocus:false,
48219     /**
48220      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48221      */
48222     queryParam: 'query',
48223     /**
48224      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48225      * when mode = 'remote' (defaults to 'Loading...')
48226      */
48227     loadingText: 'Loading...',
48228     /**
48229      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48230      */
48231     resizable: false,
48232     /**
48233      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48234      */
48235     handleHeight : 8,
48236     /**
48237      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48238      * traditional select (defaults to true)
48239      */
48240     editable: true,
48241     /**
48242      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48243      */
48244     allQuery: '',
48245     /**
48246      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48247      */
48248     mode: 'remote',
48249     /**
48250      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48251      * listWidth has a higher value)
48252      */
48253     minListWidth : 70,
48254     /**
48255      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48256      * allow the user to set arbitrary text into the field (defaults to false)
48257      */
48258     forceSelection:false,
48259     /**
48260      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48261      * if typeAhead = true (defaults to 250)
48262      */
48263     typeAheadDelay : 250,
48264     /**
48265      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48266      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48267      */
48268     valueNotFoundText : undefined,
48269     
48270     /**
48271      * @cfg {String} defaultValue The value displayed after loading the store.
48272      */
48273     defaultValue: '',
48274     
48275     /**
48276      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48277      */
48278     blockFocus : false,
48279     
48280     /**
48281      * @cfg {Boolean} disableClear Disable showing of clear button.
48282      */
48283     disableClear : false,
48284     /**
48285      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48286      */
48287     alwaysQuery : false,
48288     
48289     //private
48290     addicon : false,
48291     editicon: false,
48292     
48293     // element that contains real text value.. (when hidden is used..)
48294      
48295     // private
48296     onRender : function(ct, position){
48297         Roo.form.Field.prototype.onRender.call(this, ct, position);
48298         
48299         if(this.store){
48300             this.store.on('beforeload', this.onBeforeLoad, this);
48301             this.store.on('load', this.onLoad, this);
48302             this.store.on('loadexception', this.onLoadException, this);
48303             this.store.load({});
48304         }
48305         
48306         
48307         
48308     },
48309
48310     // private
48311     initEvents : function(){
48312         //Roo.form.ComboBox.superclass.initEvents.call(this);
48313  
48314     },
48315
48316     onDestroy : function(){
48317        
48318         if(this.store){
48319             this.store.un('beforeload', this.onBeforeLoad, this);
48320             this.store.un('load', this.onLoad, this);
48321             this.store.un('loadexception', this.onLoadException, this);
48322         }
48323         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48324     },
48325
48326     // private
48327     fireKey : function(e){
48328         if(e.isNavKeyPress() && !this.list.isVisible()){
48329             this.fireEvent("specialkey", this, e);
48330         }
48331     },
48332
48333     // private
48334     onResize: function(w, h){
48335         
48336         return; 
48337     
48338         
48339     },
48340
48341     /**
48342      * Allow or prevent the user from directly editing the field text.  If false is passed,
48343      * the user will only be able to select from the items defined in the dropdown list.  This method
48344      * is the runtime equivalent of setting the 'editable' config option at config time.
48345      * @param {Boolean} value True to allow the user to directly edit the field text
48346      */
48347     setEditable : function(value){
48348          
48349     },
48350
48351     // private
48352     onBeforeLoad : function(){
48353         
48354         Roo.log("Select before load");
48355         return;
48356     
48357         this.innerList.update(this.loadingText ?
48358                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48359         //this.restrictHeight();
48360         this.selectedIndex = -1;
48361     },
48362
48363     // private
48364     onLoad : function(){
48365
48366     
48367         var dom = this.el.dom;
48368         dom.innerHTML = '';
48369          var od = dom.ownerDocument;
48370          
48371         if (this.emptyText) {
48372             var op = od.createElement('option');
48373             op.setAttribute('value', '');
48374             op.innerHTML = String.format('{0}', this.emptyText);
48375             dom.appendChild(op);
48376         }
48377         if(this.store.getCount() > 0){
48378            
48379             var vf = this.valueField;
48380             var df = this.displayField;
48381             this.store.data.each(function(r) {
48382                 // which colmsn to use... testing - cdoe / title..
48383                 var op = od.createElement('option');
48384                 op.setAttribute('value', r.data[vf]);
48385                 op.innerHTML = String.format('{0}', r.data[df]);
48386                 dom.appendChild(op);
48387             });
48388             if (typeof(this.defaultValue != 'undefined')) {
48389                 this.setValue(this.defaultValue);
48390             }
48391             
48392              
48393         }else{
48394             //this.onEmptyResults();
48395         }
48396         //this.el.focus();
48397     },
48398     // private
48399     onLoadException : function()
48400     {
48401         dom.innerHTML = '';
48402             
48403         Roo.log("Select on load exception");
48404         return;
48405     
48406         this.collapse();
48407         Roo.log(this.store.reader.jsonData);
48408         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48409             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48410         }
48411         
48412         
48413     },
48414     // private
48415     onTypeAhead : function(){
48416          
48417     },
48418
48419     // private
48420     onSelect : function(record, index){
48421         Roo.log('on select?');
48422         return;
48423         if(this.fireEvent('beforeselect', this, record, index) !== false){
48424             this.setFromData(index > -1 ? record.data : false);
48425             this.collapse();
48426             this.fireEvent('select', this, record, index);
48427         }
48428     },
48429
48430     /**
48431      * Returns the currently selected field value or empty string if no value is set.
48432      * @return {String} value The selected value
48433      */
48434     getValue : function(){
48435         var dom = this.el.dom;
48436         this.value = dom.options[dom.selectedIndex].value;
48437         return this.value;
48438         
48439     },
48440
48441     /**
48442      * Clears any text/value currently set in the field
48443      */
48444     clearValue : function(){
48445         this.value = '';
48446         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48447         
48448     },
48449
48450     /**
48451      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48452      * will be displayed in the field.  If the value does not match the data value of an existing item,
48453      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48454      * Otherwise the field will be blank (although the value will still be set).
48455      * @param {String} value The value to match
48456      */
48457     setValue : function(v){
48458         var d = this.el.dom;
48459         for (var i =0; i < d.options.length;i++) {
48460             if (v == d.options[i].value) {
48461                 d.selectedIndex = i;
48462                 this.value = v;
48463                 return;
48464             }
48465         }
48466         this.clearValue();
48467     },
48468     /**
48469      * @property {Object} the last set data for the element
48470      */
48471     
48472     lastData : false,
48473     /**
48474      * Sets the value of the field based on a object which is related to the record format for the store.
48475      * @param {Object} value the value to set as. or false on reset?
48476      */
48477     setFromData : function(o){
48478         Roo.log('setfrom data?');
48479          
48480         
48481         
48482     },
48483     // private
48484     reset : function(){
48485         this.clearValue();
48486     },
48487     // private
48488     findRecord : function(prop, value){
48489         
48490         return false;
48491     
48492         var record;
48493         if(this.store.getCount() > 0){
48494             this.store.each(function(r){
48495                 if(r.data[prop] == value){
48496                     record = r;
48497                     return false;
48498                 }
48499                 return true;
48500             });
48501         }
48502         return record;
48503     },
48504     
48505     getName: function()
48506     {
48507         // returns hidden if it's set..
48508         if (!this.rendered) {return ''};
48509         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48510         
48511     },
48512      
48513
48514     
48515
48516     // private
48517     onEmptyResults : function(){
48518         Roo.log('empty results');
48519         //this.collapse();
48520     },
48521
48522     /**
48523      * Returns true if the dropdown list is expanded, else false.
48524      */
48525     isExpanded : function(){
48526         return false;
48527     },
48528
48529     /**
48530      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48531      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48532      * @param {String} value The data value of the item to select
48533      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48534      * selected item if it is not currently in view (defaults to true)
48535      * @return {Boolean} True if the value matched an item in the list, else false
48536      */
48537     selectByValue : function(v, scrollIntoView){
48538         Roo.log('select By Value');
48539         return false;
48540     
48541         if(v !== undefined && v !== null){
48542             var r = this.findRecord(this.valueField || this.displayField, v);
48543             if(r){
48544                 this.select(this.store.indexOf(r), scrollIntoView);
48545                 return true;
48546             }
48547         }
48548         return false;
48549     },
48550
48551     /**
48552      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48553      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48554      * @param {Number} index The zero-based index of the list item to select
48555      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48556      * selected item if it is not currently in view (defaults to true)
48557      */
48558     select : function(index, scrollIntoView){
48559         Roo.log('select ');
48560         return  ;
48561         
48562         this.selectedIndex = index;
48563         this.view.select(index);
48564         if(scrollIntoView !== false){
48565             var el = this.view.getNode(index);
48566             if(el){
48567                 this.innerList.scrollChildIntoView(el, false);
48568             }
48569         }
48570     },
48571
48572       
48573
48574     // private
48575     validateBlur : function(){
48576         
48577         return;
48578         
48579     },
48580
48581     // private
48582     initQuery : function(){
48583         this.doQuery(this.getRawValue());
48584     },
48585
48586     // private
48587     doForce : function(){
48588         if(this.el.dom.value.length > 0){
48589             this.el.dom.value =
48590                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48591              
48592         }
48593     },
48594
48595     /**
48596      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48597      * query allowing the query action to be canceled if needed.
48598      * @param {String} query The SQL query to execute
48599      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48600      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48601      * saved in the current store (defaults to false)
48602      */
48603     doQuery : function(q, forceAll){
48604         
48605         Roo.log('doQuery?');
48606         if(q === undefined || q === null){
48607             q = '';
48608         }
48609         var qe = {
48610             query: q,
48611             forceAll: forceAll,
48612             combo: this,
48613             cancel:false
48614         };
48615         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48616             return false;
48617         }
48618         q = qe.query;
48619         forceAll = qe.forceAll;
48620         if(forceAll === true || (q.length >= this.minChars)){
48621             if(this.lastQuery != q || this.alwaysQuery){
48622                 this.lastQuery = q;
48623                 if(this.mode == 'local'){
48624                     this.selectedIndex = -1;
48625                     if(forceAll){
48626                         this.store.clearFilter();
48627                     }else{
48628                         this.store.filter(this.displayField, q);
48629                     }
48630                     this.onLoad();
48631                 }else{
48632                     this.store.baseParams[this.queryParam] = q;
48633                     this.store.load({
48634                         params: this.getParams(q)
48635                     });
48636                     this.expand();
48637                 }
48638             }else{
48639                 this.selectedIndex = -1;
48640                 this.onLoad();   
48641             }
48642         }
48643     },
48644
48645     // private
48646     getParams : function(q){
48647         var p = {};
48648         //p[this.queryParam] = q;
48649         if(this.pageSize){
48650             p.start = 0;
48651             p.limit = this.pageSize;
48652         }
48653         return p;
48654     },
48655
48656     /**
48657      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48658      */
48659     collapse : function(){
48660         
48661     },
48662
48663     // private
48664     collapseIf : function(e){
48665         
48666     },
48667
48668     /**
48669      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48670      */
48671     expand : function(){
48672         
48673     } ,
48674
48675     // private
48676      
48677
48678     /** 
48679     * @cfg {Boolean} grow 
48680     * @hide 
48681     */
48682     /** 
48683     * @cfg {Number} growMin 
48684     * @hide 
48685     */
48686     /** 
48687     * @cfg {Number} growMax 
48688     * @hide 
48689     */
48690     /**
48691      * @hide
48692      * @method autoSize
48693      */
48694     
48695     setWidth : function()
48696     {
48697         
48698     },
48699     getResizeEl : function(){
48700         return this.el;
48701     }
48702 });//<script type="text/javasscript">
48703  
48704
48705 /**
48706  * @class Roo.DDView
48707  * A DnD enabled version of Roo.View.
48708  * @param {Element/String} container The Element in which to create the View.
48709  * @param {String} tpl The template string used to create the markup for each element of the View
48710  * @param {Object} config The configuration properties. These include all the config options of
48711  * {@link Roo.View} plus some specific to this class.<br>
48712  * <p>
48713  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48714  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48715  * <p>
48716  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48717 .x-view-drag-insert-above {
48718         border-top:1px dotted #3366cc;
48719 }
48720 .x-view-drag-insert-below {
48721         border-bottom:1px dotted #3366cc;
48722 }
48723 </code></pre>
48724  * 
48725  */
48726  
48727 Roo.DDView = function(container, tpl, config) {
48728     Roo.DDView.superclass.constructor.apply(this, arguments);
48729     this.getEl().setStyle("outline", "0px none");
48730     this.getEl().unselectable();
48731     if (this.dragGroup) {
48732                 this.setDraggable(this.dragGroup.split(","));
48733     }
48734     if (this.dropGroup) {
48735                 this.setDroppable(this.dropGroup.split(","));
48736     }
48737     if (this.deletable) {
48738         this.setDeletable();
48739     }
48740     this.isDirtyFlag = false;
48741         this.addEvents({
48742                 "drop" : true
48743         });
48744 };
48745
48746 Roo.extend(Roo.DDView, Roo.View, {
48747 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48748 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48749 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48750 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48751
48752         isFormField: true,
48753
48754         reset: Roo.emptyFn,
48755         
48756         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48757
48758         validate: function() {
48759                 return true;
48760         },
48761         
48762         destroy: function() {
48763                 this.purgeListeners();
48764                 this.getEl.removeAllListeners();
48765                 this.getEl().remove();
48766                 if (this.dragZone) {
48767                         if (this.dragZone.destroy) {
48768                                 this.dragZone.destroy();
48769                         }
48770                 }
48771                 if (this.dropZone) {
48772                         if (this.dropZone.destroy) {
48773                                 this.dropZone.destroy();
48774                         }
48775                 }
48776         },
48777
48778 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48779         getName: function() {
48780                 return this.name;
48781         },
48782
48783 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48784         setValue: function(v) {
48785                 if (!this.store) {
48786                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48787                 }
48788                 var data = {};
48789                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48790                 this.store.proxy = new Roo.data.MemoryProxy(data);
48791                 this.store.load();
48792         },
48793
48794 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48795         getValue: function() {
48796                 var result = '(';
48797                 this.store.each(function(rec) {
48798                         result += rec.id + ',';
48799                 });
48800                 return result.substr(0, result.length - 1) + ')';
48801         },
48802         
48803         getIds: function() {
48804                 var i = 0, result = new Array(this.store.getCount());
48805                 this.store.each(function(rec) {
48806                         result[i++] = rec.id;
48807                 });
48808                 return result;
48809         },
48810         
48811         isDirty: function() {
48812                 return this.isDirtyFlag;
48813         },
48814
48815 /**
48816  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48817  *      whole Element becomes the target, and this causes the drop gesture to append.
48818  */
48819     getTargetFromEvent : function(e) {
48820                 var target = e.getTarget();
48821                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48822                 target = target.parentNode;
48823                 }
48824                 if (!target) {
48825                         target = this.el.dom.lastChild || this.el.dom;
48826                 }
48827                 return target;
48828     },
48829
48830 /**
48831  *      Create the drag data which consists of an object which has the property "ddel" as
48832  *      the drag proxy element. 
48833  */
48834     getDragData : function(e) {
48835         var target = this.findItemFromChild(e.getTarget());
48836                 if(target) {
48837                         this.handleSelection(e);
48838                         var selNodes = this.getSelectedNodes();
48839             var dragData = {
48840                 source: this,
48841                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48842                 nodes: selNodes,
48843                 records: []
48844                         };
48845                         var selectedIndices = this.getSelectedIndexes();
48846                         for (var i = 0; i < selectedIndices.length; i++) {
48847                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48848                         }
48849                         if (selNodes.length == 1) {
48850                                 dragData.ddel = target.cloneNode(true); // the div element
48851                         } else {
48852                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48853                                 div.className = 'multi-proxy';
48854                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48855                                         div.appendChild(selNodes[i].cloneNode(true));
48856                                 }
48857                                 dragData.ddel = div;
48858                         }
48859             //console.log(dragData)
48860             //console.log(dragData.ddel.innerHTML)
48861                         return dragData;
48862                 }
48863         //console.log('nodragData')
48864                 return false;
48865     },
48866     
48867 /**     Specify to which ddGroup items in this DDView may be dragged. */
48868     setDraggable: function(ddGroup) {
48869         if (ddGroup instanceof Array) {
48870                 Roo.each(ddGroup, this.setDraggable, this);
48871                 return;
48872         }
48873         if (this.dragZone) {
48874                 this.dragZone.addToGroup(ddGroup);
48875         } else {
48876                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48877                                 containerScroll: true,
48878                                 ddGroup: ddGroup 
48879
48880                         });
48881 //                      Draggability implies selection. DragZone's mousedown selects the element.
48882                         if (!this.multiSelect) { this.singleSelect = true; }
48883
48884 //                      Wire the DragZone's handlers up to methods in *this*
48885                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48886                 }
48887     },
48888
48889 /**     Specify from which ddGroup this DDView accepts drops. */
48890     setDroppable: function(ddGroup) {
48891         if (ddGroup instanceof Array) {
48892                 Roo.each(ddGroup, this.setDroppable, this);
48893                 return;
48894         }
48895         if (this.dropZone) {
48896                 this.dropZone.addToGroup(ddGroup);
48897         } else {
48898                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48899                                 containerScroll: true,
48900                                 ddGroup: ddGroup
48901                         });
48902
48903 //                      Wire the DropZone's handlers up to methods in *this*
48904                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48905                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48906                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48907                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48908                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48909                 }
48910     },
48911
48912 /**     Decide whether to drop above or below a View node. */
48913     getDropPoint : function(e, n, dd){
48914         if (n == this.el.dom) { return "above"; }
48915                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48916                 var c = t + (b - t) / 2;
48917                 var y = Roo.lib.Event.getPageY(e);
48918                 if(y <= c) {
48919                         return "above";
48920                 }else{
48921                         return "below";
48922                 }
48923     },
48924
48925     onNodeEnter : function(n, dd, e, data){
48926                 return false;
48927     },
48928     
48929     onNodeOver : function(n, dd, e, data){
48930                 var pt = this.getDropPoint(e, n, dd);
48931                 // set the insert point style on the target node
48932                 var dragElClass = this.dropNotAllowed;
48933                 if (pt) {
48934                         var targetElClass;
48935                         if (pt == "above"){
48936                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48937                                 targetElClass = "x-view-drag-insert-above";
48938                         } else {
48939                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48940                                 targetElClass = "x-view-drag-insert-below";
48941                         }
48942                         if (this.lastInsertClass != targetElClass){
48943                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48944                                 this.lastInsertClass = targetElClass;
48945                         }
48946                 }
48947                 return dragElClass;
48948         },
48949
48950     onNodeOut : function(n, dd, e, data){
48951                 this.removeDropIndicators(n);
48952     },
48953
48954     onNodeDrop : function(n, dd, e, data){
48955         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48956                 return false;
48957         }
48958         var pt = this.getDropPoint(e, n, dd);
48959                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48960                 if (pt == "below") { insertAt++; }
48961                 for (var i = 0; i < data.records.length; i++) {
48962                         var r = data.records[i];
48963                         var dup = this.store.getById(r.id);
48964                         if (dup && (dd != this.dragZone)) {
48965                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48966                         } else {
48967                                 if (data.copy) {
48968                                         this.store.insert(insertAt++, r.copy());
48969                                 } else {
48970                                         data.source.isDirtyFlag = true;
48971                                         r.store.remove(r);
48972                                         this.store.insert(insertAt++, r);
48973                                 }
48974                                 this.isDirtyFlag = true;
48975                         }
48976                 }
48977                 this.dragZone.cachedTarget = null;
48978                 return true;
48979     },
48980
48981     removeDropIndicators : function(n){
48982                 if(n){
48983                         Roo.fly(n).removeClass([
48984                                 "x-view-drag-insert-above",
48985                                 "x-view-drag-insert-below"]);
48986                         this.lastInsertClass = "_noclass";
48987                 }
48988     },
48989
48990 /**
48991  *      Utility method. Add a delete option to the DDView's context menu.
48992  *      @param {String} imageUrl The URL of the "delete" icon image.
48993  */
48994         setDeletable: function(imageUrl) {
48995                 if (!this.singleSelect && !this.multiSelect) {
48996                         this.singleSelect = true;
48997                 }
48998                 var c = this.getContextMenu();
48999                 this.contextMenu.on("itemclick", function(item) {
49000                         switch (item.id) {
49001                                 case "delete":
49002                                         this.remove(this.getSelectedIndexes());
49003                                         break;
49004                         }
49005                 }, this);
49006                 this.contextMenu.add({
49007                         icon: imageUrl,
49008                         id: "delete",
49009                         text: 'Delete'
49010                 });
49011         },
49012         
49013 /**     Return the context menu for this DDView. */
49014         getContextMenu: function() {
49015                 if (!this.contextMenu) {
49016 //                      Create the View's context menu
49017                         this.contextMenu = new Roo.menu.Menu({
49018                                 id: this.id + "-contextmenu"
49019                         });
49020                         this.el.on("contextmenu", this.showContextMenu, this);
49021                 }
49022                 return this.contextMenu;
49023         },
49024         
49025         disableContextMenu: function() {
49026                 if (this.contextMenu) {
49027                         this.el.un("contextmenu", this.showContextMenu, this);
49028                 }
49029         },
49030
49031         showContextMenu: function(e, item) {
49032         item = this.findItemFromChild(e.getTarget());
49033                 if (item) {
49034                         e.stopEvent();
49035                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
49036                         this.contextMenu.showAt(e.getXY());
49037             }
49038     },
49039
49040 /**
49041  *      Remove {@link Roo.data.Record}s at the specified indices.
49042  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49043  */
49044     remove: function(selectedIndices) {
49045                 selectedIndices = [].concat(selectedIndices);
49046                 for (var i = 0; i < selectedIndices.length; i++) {
49047                         var rec = this.store.getAt(selectedIndices[i]);
49048                         this.store.remove(rec);
49049                 }
49050     },
49051
49052 /**
49053  *      Double click fires the event, but also, if this is draggable, and there is only one other
49054  *      related DropZone, it transfers the selected node.
49055  */
49056     onDblClick : function(e){
49057         var item = this.findItemFromChild(e.getTarget());
49058         if(item){
49059             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49060                 return false;
49061             }
49062             if (this.dragGroup) {
49063                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49064                     while (targets.indexOf(this.dropZone) > -1) {
49065                             targets.remove(this.dropZone);
49066                                 }
49067                     if (targets.length == 1) {
49068                                         this.dragZone.cachedTarget = null;
49069                         var el = Roo.get(targets[0].getEl());
49070                         var box = el.getBox(true);
49071                         targets[0].onNodeDrop(el.dom, {
49072                                 target: el.dom,
49073                                 xy: [box.x, box.y + box.height - 1]
49074                         }, null, this.getDragData(e));
49075                     }
49076                 }
49077         }
49078     },
49079     
49080     handleSelection: function(e) {
49081                 this.dragZone.cachedTarget = null;
49082         var item = this.findItemFromChild(e.getTarget());
49083         if (!item) {
49084                 this.clearSelections(true);
49085                 return;
49086         }
49087                 if (item && (this.multiSelect || this.singleSelect)){
49088                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49089                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49090                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49091                                 this.unselect(item);
49092                         } else {
49093                                 this.select(item, this.multiSelect && e.ctrlKey);
49094                                 this.lastSelection = item;
49095                         }
49096                 }
49097     },
49098
49099     onItemClick : function(item, index, e){
49100                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49101                         return false;
49102                 }
49103                 return true;
49104     },
49105
49106     unselect : function(nodeInfo, suppressEvent){
49107                 var node = this.getNode(nodeInfo);
49108                 if(node && this.isSelected(node)){
49109                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49110                                 Roo.fly(node).removeClass(this.selectedClass);
49111                                 this.selections.remove(node);
49112                                 if(!suppressEvent){
49113                                         this.fireEvent("selectionchange", this, this.selections);
49114                                 }
49115                         }
49116                 }
49117     }
49118 });
49119 /*
49120  * Based on:
49121  * Ext JS Library 1.1.1
49122  * Copyright(c) 2006-2007, Ext JS, LLC.
49123  *
49124  * Originally Released Under LGPL - original licence link has changed is not relivant.
49125  *
49126  * Fork - LGPL
49127  * <script type="text/javascript">
49128  */
49129  
49130 /**
49131  * @class Roo.LayoutManager
49132  * @extends Roo.util.Observable
49133  * Base class for layout managers.
49134  */
49135 Roo.LayoutManager = function(container, config){
49136     Roo.LayoutManager.superclass.constructor.call(this);
49137     this.el = Roo.get(container);
49138     // ie scrollbar fix
49139     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49140         document.body.scroll = "no";
49141     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49142         this.el.position('relative');
49143     }
49144     this.id = this.el.id;
49145     this.el.addClass("x-layout-container");
49146     /** false to disable window resize monitoring @type Boolean */
49147     this.monitorWindowResize = true;
49148     this.regions = {};
49149     this.addEvents({
49150         /**
49151          * @event layout
49152          * Fires when a layout is performed. 
49153          * @param {Roo.LayoutManager} this
49154          */
49155         "layout" : true,
49156         /**
49157          * @event regionresized
49158          * Fires when the user resizes a region. 
49159          * @param {Roo.LayoutRegion} region The resized region
49160          * @param {Number} newSize The new size (width for east/west, height for north/south)
49161          */
49162         "regionresized" : true,
49163         /**
49164          * @event regioncollapsed
49165          * Fires when a region is collapsed. 
49166          * @param {Roo.LayoutRegion} region The collapsed region
49167          */
49168         "regioncollapsed" : true,
49169         /**
49170          * @event regionexpanded
49171          * Fires when a region is expanded.  
49172          * @param {Roo.LayoutRegion} region The expanded region
49173          */
49174         "regionexpanded" : true
49175     });
49176     this.updating = false;
49177     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49178 };
49179
49180 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49181     /**
49182      * Returns true if this layout is currently being updated
49183      * @return {Boolean}
49184      */
49185     isUpdating : function(){
49186         return this.updating; 
49187     },
49188     
49189     /**
49190      * Suspend the LayoutManager from doing auto-layouts while
49191      * making multiple add or remove calls
49192      */
49193     beginUpdate : function(){
49194         this.updating = true;    
49195     },
49196     
49197     /**
49198      * Restore auto-layouts and optionally disable the manager from performing a layout
49199      * @param {Boolean} noLayout true to disable a layout update 
49200      */
49201     endUpdate : function(noLayout){
49202         this.updating = false;
49203         if(!noLayout){
49204             this.layout();
49205         }    
49206     },
49207     
49208     layout: function(){
49209         
49210     },
49211     
49212     onRegionResized : function(region, newSize){
49213         this.fireEvent("regionresized", region, newSize);
49214         this.layout();
49215     },
49216     
49217     onRegionCollapsed : function(region){
49218         this.fireEvent("regioncollapsed", region);
49219     },
49220     
49221     onRegionExpanded : function(region){
49222         this.fireEvent("regionexpanded", region);
49223     },
49224         
49225     /**
49226      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49227      * performs box-model adjustments.
49228      * @return {Object} The size as an object {width: (the width), height: (the height)}
49229      */
49230     getViewSize : function(){
49231         var size;
49232         if(this.el.dom != document.body){
49233             size = this.el.getSize();
49234         }else{
49235             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49236         }
49237         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49238         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49239         return size;
49240     },
49241     
49242     /**
49243      * Returns the Element this layout is bound to.
49244      * @return {Roo.Element}
49245      */
49246     getEl : function(){
49247         return this.el;
49248     },
49249     
49250     /**
49251      * Returns the specified region.
49252      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49253      * @return {Roo.LayoutRegion}
49254      */
49255     getRegion : function(target){
49256         return this.regions[target.toLowerCase()];
49257     },
49258     
49259     onWindowResize : function(){
49260         if(this.monitorWindowResize){
49261             this.layout();
49262         }
49263     }
49264 });/*
49265  * Based on:
49266  * Ext JS Library 1.1.1
49267  * Copyright(c) 2006-2007, Ext JS, LLC.
49268  *
49269  * Originally Released Under LGPL - original licence link has changed is not relivant.
49270  *
49271  * Fork - LGPL
49272  * <script type="text/javascript">
49273  */
49274 /**
49275  * @class Roo.BorderLayout
49276  * @extends Roo.LayoutManager
49277  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49278  * please see: <br><br>
49279  * <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>
49280  * <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>
49281  * Example:
49282  <pre><code>
49283  var layout = new Roo.BorderLayout(document.body, {
49284     north: {
49285         initialSize: 25,
49286         titlebar: false
49287     },
49288     west: {
49289         split:true,
49290         initialSize: 200,
49291         minSize: 175,
49292         maxSize: 400,
49293         titlebar: true,
49294         collapsible: true
49295     },
49296     east: {
49297         split:true,
49298         initialSize: 202,
49299         minSize: 175,
49300         maxSize: 400,
49301         titlebar: true,
49302         collapsible: true
49303     },
49304     south: {
49305         split:true,
49306         initialSize: 100,
49307         minSize: 100,
49308         maxSize: 200,
49309         titlebar: true,
49310         collapsible: true
49311     },
49312     center: {
49313         titlebar: true,
49314         autoScroll:true,
49315         resizeTabs: true,
49316         minTabWidth: 50,
49317         preferredTabWidth: 150
49318     }
49319 });
49320
49321 // shorthand
49322 var CP = Roo.ContentPanel;
49323
49324 layout.beginUpdate();
49325 layout.add("north", new CP("north", "North"));
49326 layout.add("south", new CP("south", {title: "South", closable: true}));
49327 layout.add("west", new CP("west", {title: "West"}));
49328 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49329 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49330 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49331 layout.getRegion("center").showPanel("center1");
49332 layout.endUpdate();
49333 </code></pre>
49334
49335 <b>The container the layout is rendered into can be either the body element or any other element.
49336 If it is not the body element, the container needs to either be an absolute positioned element,
49337 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49338 the container size if it is not the body element.</b>
49339
49340 * @constructor
49341 * Create a new BorderLayout
49342 * @param {String/HTMLElement/Element} container The container this layout is bound to
49343 * @param {Object} config Configuration options
49344  */
49345 Roo.BorderLayout = function(container, config){
49346     config = config || {};
49347     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49348     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49349     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49350         var target = this.factory.validRegions[i];
49351         if(config[target]){
49352             this.addRegion(target, config[target]);
49353         }
49354     }
49355 };
49356
49357 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49358     /**
49359      * Creates and adds a new region if it doesn't already exist.
49360      * @param {String} target The target region key (north, south, east, west or center).
49361      * @param {Object} config The regions config object
49362      * @return {BorderLayoutRegion} The new region
49363      */
49364     addRegion : function(target, config){
49365         if(!this.regions[target]){
49366             var r = this.factory.create(target, this, config);
49367             this.bindRegion(target, r);
49368         }
49369         return this.regions[target];
49370     },
49371
49372     // private (kinda)
49373     bindRegion : function(name, r){
49374         this.regions[name] = r;
49375         r.on("visibilitychange", this.layout, this);
49376         r.on("paneladded", this.layout, this);
49377         r.on("panelremoved", this.layout, this);
49378         r.on("invalidated", this.layout, this);
49379         r.on("resized", this.onRegionResized, this);
49380         r.on("collapsed", this.onRegionCollapsed, this);
49381         r.on("expanded", this.onRegionExpanded, this);
49382     },
49383
49384     /**
49385      * Performs a layout update.
49386      */
49387     layout : function(){
49388         if(this.updating) return;
49389         var size = this.getViewSize();
49390         var w = size.width;
49391         var h = size.height;
49392         var centerW = w;
49393         var centerH = h;
49394         var centerY = 0;
49395         var centerX = 0;
49396         //var x = 0, y = 0;
49397
49398         var rs = this.regions;
49399         var north = rs["north"];
49400         var south = rs["south"]; 
49401         var west = rs["west"];
49402         var east = rs["east"];
49403         var center = rs["center"];
49404         //if(this.hideOnLayout){ // not supported anymore
49405             //c.el.setStyle("display", "none");
49406         //}
49407         if(north && north.isVisible()){
49408             var b = north.getBox();
49409             var m = north.getMargins();
49410             b.width = w - (m.left+m.right);
49411             b.x = m.left;
49412             b.y = m.top;
49413             centerY = b.height + b.y + m.bottom;
49414             centerH -= centerY;
49415             north.updateBox(this.safeBox(b));
49416         }
49417         if(south && south.isVisible()){
49418             var b = south.getBox();
49419             var m = south.getMargins();
49420             b.width = w - (m.left+m.right);
49421             b.x = m.left;
49422             var totalHeight = (b.height + m.top + m.bottom);
49423             b.y = h - totalHeight + m.top;
49424             centerH -= totalHeight;
49425             south.updateBox(this.safeBox(b));
49426         }
49427         if(west && west.isVisible()){
49428             var b = west.getBox();
49429             var m = west.getMargins();
49430             b.height = centerH - (m.top+m.bottom);
49431             b.x = m.left;
49432             b.y = centerY + m.top;
49433             var totalWidth = (b.width + m.left + m.right);
49434             centerX += totalWidth;
49435             centerW -= totalWidth;
49436             west.updateBox(this.safeBox(b));
49437         }
49438         if(east && east.isVisible()){
49439             var b = east.getBox();
49440             var m = east.getMargins();
49441             b.height = centerH - (m.top+m.bottom);
49442             var totalWidth = (b.width + m.left + m.right);
49443             b.x = w - totalWidth + m.left;
49444             b.y = centerY + m.top;
49445             centerW -= totalWidth;
49446             east.updateBox(this.safeBox(b));
49447         }
49448         if(center){
49449             var m = center.getMargins();
49450             var centerBox = {
49451                 x: centerX + m.left,
49452                 y: centerY + m.top,
49453                 width: centerW - (m.left+m.right),
49454                 height: centerH - (m.top+m.bottom)
49455             };
49456             //if(this.hideOnLayout){
49457                 //center.el.setStyle("display", "block");
49458             //}
49459             center.updateBox(this.safeBox(centerBox));
49460         }
49461         this.el.repaint();
49462         this.fireEvent("layout", this);
49463     },
49464
49465     // private
49466     safeBox : function(box){
49467         box.width = Math.max(0, box.width);
49468         box.height = Math.max(0, box.height);
49469         return box;
49470     },
49471
49472     /**
49473      * Adds a ContentPanel (or subclass) to this layout.
49474      * @param {String} target The target region key (north, south, east, west or center).
49475      * @param {Roo.ContentPanel} panel The panel to add
49476      * @return {Roo.ContentPanel} The added panel
49477      */
49478     add : function(target, panel){
49479          
49480         target = target.toLowerCase();
49481         return this.regions[target].add(panel);
49482     },
49483
49484     /**
49485      * Remove a ContentPanel (or subclass) to this layout.
49486      * @param {String} target The target region key (north, south, east, west or center).
49487      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49488      * @return {Roo.ContentPanel} The removed panel
49489      */
49490     remove : function(target, panel){
49491         target = target.toLowerCase();
49492         return this.regions[target].remove(panel);
49493     },
49494
49495     /**
49496      * Searches all regions for a panel with the specified id
49497      * @param {String} panelId
49498      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49499      */
49500     findPanel : function(panelId){
49501         var rs = this.regions;
49502         for(var target in rs){
49503             if(typeof rs[target] != "function"){
49504                 var p = rs[target].getPanel(panelId);
49505                 if(p){
49506                     return p;
49507                 }
49508             }
49509         }
49510         return null;
49511     },
49512
49513     /**
49514      * Searches all regions for a panel with the specified id and activates (shows) it.
49515      * @param {String/ContentPanel} panelId The panels id or the panel itself
49516      * @return {Roo.ContentPanel} The shown panel or null
49517      */
49518     showPanel : function(panelId) {
49519       var rs = this.regions;
49520       for(var target in rs){
49521          var r = rs[target];
49522          if(typeof r != "function"){
49523             if(r.hasPanel(panelId)){
49524                return r.showPanel(panelId);
49525             }
49526          }
49527       }
49528       return null;
49529    },
49530
49531    /**
49532      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49533      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49534      */
49535     restoreState : function(provider){
49536         if(!provider){
49537             provider = Roo.state.Manager;
49538         }
49539         var sm = new Roo.LayoutStateManager();
49540         sm.init(this, provider);
49541     },
49542
49543     /**
49544      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49545      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49546      * a valid ContentPanel config object.  Example:
49547      * <pre><code>
49548 // Create the main layout
49549 var layout = new Roo.BorderLayout('main-ct', {
49550     west: {
49551         split:true,
49552         minSize: 175,
49553         titlebar: true
49554     },
49555     center: {
49556         title:'Components'
49557     }
49558 }, 'main-ct');
49559
49560 // Create and add multiple ContentPanels at once via configs
49561 layout.batchAdd({
49562    west: {
49563        id: 'source-files',
49564        autoCreate:true,
49565        title:'Ext Source Files',
49566        autoScroll:true,
49567        fitToFrame:true
49568    },
49569    center : {
49570        el: cview,
49571        autoScroll:true,
49572        fitToFrame:true,
49573        toolbar: tb,
49574        resizeEl:'cbody'
49575    }
49576 });
49577 </code></pre>
49578      * @param {Object} regions An object containing ContentPanel configs by region name
49579      */
49580     batchAdd : function(regions){
49581         this.beginUpdate();
49582         for(var rname in regions){
49583             var lr = this.regions[rname];
49584             if(lr){
49585                 this.addTypedPanels(lr, regions[rname]);
49586             }
49587         }
49588         this.endUpdate();
49589     },
49590
49591     // private
49592     addTypedPanels : function(lr, ps){
49593         if(typeof ps == 'string'){
49594             lr.add(new Roo.ContentPanel(ps));
49595         }
49596         else if(ps instanceof Array){
49597             for(var i =0, len = ps.length; i < len; i++){
49598                 this.addTypedPanels(lr, ps[i]);
49599             }
49600         }
49601         else if(!ps.events){ // raw config?
49602             var el = ps.el;
49603             delete ps.el; // prevent conflict
49604             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49605         }
49606         else {  // panel object assumed!
49607             lr.add(ps);
49608         }
49609     },
49610     /**
49611      * Adds a xtype elements to the layout.
49612      * <pre><code>
49613
49614 layout.addxtype({
49615        xtype : 'ContentPanel',
49616        region: 'west',
49617        items: [ .... ]
49618    }
49619 );
49620
49621 layout.addxtype({
49622         xtype : 'NestedLayoutPanel',
49623         region: 'west',
49624         layout: {
49625            center: { },
49626            west: { }   
49627         },
49628         items : [ ... list of content panels or nested layout panels.. ]
49629    }
49630 );
49631 </code></pre>
49632      * @param {Object} cfg Xtype definition of item to add.
49633      */
49634     addxtype : function(cfg)
49635     {
49636         // basically accepts a pannel...
49637         // can accept a layout region..!?!?
49638         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49639         
49640         if (!cfg.xtype.match(/Panel$/)) {
49641             return false;
49642         }
49643         var ret = false;
49644         
49645         if (typeof(cfg.region) == 'undefined') {
49646             Roo.log("Failed to add Panel, region was not set");
49647             Roo.log(cfg);
49648             return false;
49649         }
49650         var region = cfg.region;
49651         delete cfg.region;
49652         
49653           
49654         var xitems = [];
49655         if (cfg.items) {
49656             xitems = cfg.items;
49657             delete cfg.items;
49658         }
49659         var nb = false;
49660         
49661         switch(cfg.xtype) 
49662         {
49663             case 'ContentPanel':  // ContentPanel (el, cfg)
49664             case 'ScrollPanel':  // ContentPanel (el, cfg)
49665             case 'ViewPanel': 
49666                 if(cfg.autoCreate) {
49667                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49668                 } else {
49669                     var el = this.el.createChild();
49670                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49671                 }
49672                 
49673                 this.add(region, ret);
49674                 break;
49675             
49676             
49677             case 'TreePanel': // our new panel!
49678                 cfg.el = this.el.createChild();
49679                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49680                 this.add(region, ret);
49681                 break;
49682             
49683             case 'NestedLayoutPanel': 
49684                 // create a new Layout (which is  a Border Layout...
49685                 var el = this.el.createChild();
49686                 var clayout = cfg.layout;
49687                 delete cfg.layout;
49688                 clayout.items   = clayout.items  || [];
49689                 // replace this exitems with the clayout ones..
49690                 xitems = clayout.items;
49691                  
49692                 
49693                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49694                     cfg.background = false;
49695                 }
49696                 var layout = new Roo.BorderLayout(el, clayout);
49697                 
49698                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49699                 //console.log('adding nested layout panel '  + cfg.toSource());
49700                 this.add(region, ret);
49701                 nb = {}; /// find first...
49702                 break;
49703                 
49704             case 'GridPanel': 
49705             
49706                 // needs grid and region
49707                 
49708                 //var el = this.getRegion(region).el.createChild();
49709                 var el = this.el.createChild();
49710                 // create the grid first...
49711                 
49712                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49713                 delete cfg.grid;
49714                 if (region == 'center' && this.active ) {
49715                     cfg.background = false;
49716                 }
49717                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49718                 
49719                 this.add(region, ret);
49720                 if (cfg.background) {
49721                     ret.on('activate', function(gp) {
49722                         if (!gp.grid.rendered) {
49723                             gp.grid.render();
49724                         }
49725                     });
49726                 } else {
49727                     grid.render();
49728                 }
49729                 break;
49730            
49731            
49732            
49733                 
49734                 
49735                 
49736             default:
49737                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49738                     
49739                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49740                     this.add(region, ret);
49741                 } else {
49742                 
49743                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49744                     return null;
49745                 }
49746                 
49747              // GridPanel (grid, cfg)
49748             
49749         }
49750         this.beginUpdate();
49751         // add children..
49752         var region = '';
49753         var abn = {};
49754         Roo.each(xitems, function(i)  {
49755             region = nb && i.region ? i.region : false;
49756             
49757             var add = ret.addxtype(i);
49758            
49759             if (region) {
49760                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49761                 if (!i.background) {
49762                     abn[region] = nb[region] ;
49763                 }
49764             }
49765             
49766         });
49767         this.endUpdate();
49768
49769         // make the last non-background panel active..
49770         //if (nb) { Roo.log(abn); }
49771         if (nb) {
49772             
49773             for(var r in abn) {
49774                 region = this.getRegion(r);
49775                 if (region) {
49776                     // tried using nb[r], but it does not work..
49777                      
49778                     region.showPanel(abn[r]);
49779                    
49780                 }
49781             }
49782         }
49783         return ret;
49784         
49785     }
49786 });
49787
49788 /**
49789  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49790  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49791  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49792  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49793  * <pre><code>
49794 // shorthand
49795 var CP = Roo.ContentPanel;
49796
49797 var layout = Roo.BorderLayout.create({
49798     north: {
49799         initialSize: 25,
49800         titlebar: false,
49801         panels: [new CP("north", "North")]
49802     },
49803     west: {
49804         split:true,
49805         initialSize: 200,
49806         minSize: 175,
49807         maxSize: 400,
49808         titlebar: true,
49809         collapsible: true,
49810         panels: [new CP("west", {title: "West"})]
49811     },
49812     east: {
49813         split:true,
49814         initialSize: 202,
49815         minSize: 175,
49816         maxSize: 400,
49817         titlebar: true,
49818         collapsible: true,
49819         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49820     },
49821     south: {
49822         split:true,
49823         initialSize: 100,
49824         minSize: 100,
49825         maxSize: 200,
49826         titlebar: true,
49827         collapsible: true,
49828         panels: [new CP("south", {title: "South", closable: true})]
49829     },
49830     center: {
49831         titlebar: true,
49832         autoScroll:true,
49833         resizeTabs: true,
49834         minTabWidth: 50,
49835         preferredTabWidth: 150,
49836         panels: [
49837             new CP("center1", {title: "Close Me", closable: true}),
49838             new CP("center2", {title: "Center Panel", closable: false})
49839         ]
49840     }
49841 }, document.body);
49842
49843 layout.getRegion("center").showPanel("center1");
49844 </code></pre>
49845  * @param config
49846  * @param targetEl
49847  */
49848 Roo.BorderLayout.create = function(config, targetEl){
49849     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49850     layout.beginUpdate();
49851     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49852     for(var j = 0, jlen = regions.length; j < jlen; j++){
49853         var lr = regions[j];
49854         if(layout.regions[lr] && config[lr].panels){
49855             var r = layout.regions[lr];
49856             var ps = config[lr].panels;
49857             layout.addTypedPanels(r, ps);
49858         }
49859     }
49860     layout.endUpdate();
49861     return layout;
49862 };
49863
49864 // private
49865 Roo.BorderLayout.RegionFactory = {
49866     // private
49867     validRegions : ["north","south","east","west","center"],
49868
49869     // private
49870     create : function(target, mgr, config){
49871         target = target.toLowerCase();
49872         if(config.lightweight || config.basic){
49873             return new Roo.BasicLayoutRegion(mgr, config, target);
49874         }
49875         switch(target){
49876             case "north":
49877                 return new Roo.NorthLayoutRegion(mgr, config);
49878             case "south":
49879                 return new Roo.SouthLayoutRegion(mgr, config);
49880             case "east":
49881                 return new Roo.EastLayoutRegion(mgr, config);
49882             case "west":
49883                 return new Roo.WestLayoutRegion(mgr, config);
49884             case "center":
49885                 return new Roo.CenterLayoutRegion(mgr, config);
49886         }
49887         throw 'Layout region "'+target+'" not supported.';
49888     }
49889 };/*
49890  * Based on:
49891  * Ext JS Library 1.1.1
49892  * Copyright(c) 2006-2007, Ext JS, LLC.
49893  *
49894  * Originally Released Under LGPL - original licence link has changed is not relivant.
49895  *
49896  * Fork - LGPL
49897  * <script type="text/javascript">
49898  */
49899  
49900 /**
49901  * @class Roo.BasicLayoutRegion
49902  * @extends Roo.util.Observable
49903  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49904  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49905  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49906  */
49907 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49908     this.mgr = mgr;
49909     this.position  = pos;
49910     this.events = {
49911         /**
49912          * @scope Roo.BasicLayoutRegion
49913          */
49914         
49915         /**
49916          * @event beforeremove
49917          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49918          * @param {Roo.LayoutRegion} this
49919          * @param {Roo.ContentPanel} panel The panel
49920          * @param {Object} e The cancel event object
49921          */
49922         "beforeremove" : true,
49923         /**
49924          * @event invalidated
49925          * Fires when the layout for this region is changed.
49926          * @param {Roo.LayoutRegion} this
49927          */
49928         "invalidated" : true,
49929         /**
49930          * @event visibilitychange
49931          * Fires when this region is shown or hidden 
49932          * @param {Roo.LayoutRegion} this
49933          * @param {Boolean} visibility true or false
49934          */
49935         "visibilitychange" : true,
49936         /**
49937          * @event paneladded
49938          * Fires when a panel is added. 
49939          * @param {Roo.LayoutRegion} this
49940          * @param {Roo.ContentPanel} panel The panel
49941          */
49942         "paneladded" : true,
49943         /**
49944          * @event panelremoved
49945          * Fires when a panel is removed. 
49946          * @param {Roo.LayoutRegion} this
49947          * @param {Roo.ContentPanel} panel The panel
49948          */
49949         "panelremoved" : true,
49950         /**
49951          * @event collapsed
49952          * Fires when this region is collapsed.
49953          * @param {Roo.LayoutRegion} this
49954          */
49955         "collapsed" : true,
49956         /**
49957          * @event expanded
49958          * Fires when this region is expanded.
49959          * @param {Roo.LayoutRegion} this
49960          */
49961         "expanded" : true,
49962         /**
49963          * @event slideshow
49964          * Fires when this region is slid into view.
49965          * @param {Roo.LayoutRegion} this
49966          */
49967         "slideshow" : true,
49968         /**
49969          * @event slidehide
49970          * Fires when this region slides out of view. 
49971          * @param {Roo.LayoutRegion} this
49972          */
49973         "slidehide" : true,
49974         /**
49975          * @event panelactivated
49976          * Fires when a panel is activated. 
49977          * @param {Roo.LayoutRegion} this
49978          * @param {Roo.ContentPanel} panel The activated panel
49979          */
49980         "panelactivated" : true,
49981         /**
49982          * @event resized
49983          * Fires when the user resizes this region. 
49984          * @param {Roo.LayoutRegion} this
49985          * @param {Number} newSize The new size (width for east/west, height for north/south)
49986          */
49987         "resized" : true
49988     };
49989     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49990     this.panels = new Roo.util.MixedCollection();
49991     this.panels.getKey = this.getPanelId.createDelegate(this);
49992     this.box = null;
49993     this.activePanel = null;
49994     // ensure listeners are added...
49995     
49996     if (config.listeners || config.events) {
49997         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49998             listeners : config.listeners || {},
49999             events : config.events || {}
50000         });
50001     }
50002     
50003     if(skipConfig !== true){
50004         this.applyConfig(config);
50005     }
50006 };
50007
50008 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
50009     getPanelId : function(p){
50010         return p.getId();
50011     },
50012     
50013     applyConfig : function(config){
50014         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50015         this.config = config;
50016         
50017     },
50018     
50019     /**
50020      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
50021      * the width, for horizontal (north, south) the height.
50022      * @param {Number} newSize The new width or height
50023      */
50024     resizeTo : function(newSize){
50025         var el = this.el ? this.el :
50026                  (this.activePanel ? this.activePanel.getEl() : null);
50027         if(el){
50028             switch(this.position){
50029                 case "east":
50030                 case "west":
50031                     el.setWidth(newSize);
50032                     this.fireEvent("resized", this, newSize);
50033                 break;
50034                 case "north":
50035                 case "south":
50036                     el.setHeight(newSize);
50037                     this.fireEvent("resized", this, newSize);
50038                 break;                
50039             }
50040         }
50041     },
50042     
50043     getBox : function(){
50044         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50045     },
50046     
50047     getMargins : function(){
50048         return this.margins;
50049     },
50050     
50051     updateBox : function(box){
50052         this.box = box;
50053         var el = this.activePanel.getEl();
50054         el.dom.style.left = box.x + "px";
50055         el.dom.style.top = box.y + "px";
50056         this.activePanel.setSize(box.width, box.height);
50057     },
50058     
50059     /**
50060      * Returns the container element for this region.
50061      * @return {Roo.Element}
50062      */
50063     getEl : function(){
50064         return this.activePanel;
50065     },
50066     
50067     /**
50068      * Returns true if this region is currently visible.
50069      * @return {Boolean}
50070      */
50071     isVisible : function(){
50072         return this.activePanel ? true : false;
50073     },
50074     
50075     setActivePanel : function(panel){
50076         panel = this.getPanel(panel);
50077         if(this.activePanel && this.activePanel != panel){
50078             this.activePanel.setActiveState(false);
50079             this.activePanel.getEl().setLeftTop(-10000,-10000);
50080         }
50081         this.activePanel = panel;
50082         panel.setActiveState(true);
50083         if(this.box){
50084             panel.setSize(this.box.width, this.box.height);
50085         }
50086         this.fireEvent("panelactivated", this, panel);
50087         this.fireEvent("invalidated");
50088     },
50089     
50090     /**
50091      * Show the specified panel.
50092      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50093      * @return {Roo.ContentPanel} The shown panel or null
50094      */
50095     showPanel : function(panel){
50096         if(panel = this.getPanel(panel)){
50097             this.setActivePanel(panel);
50098         }
50099         return panel;
50100     },
50101     
50102     /**
50103      * Get the active panel for this region.
50104      * @return {Roo.ContentPanel} The active panel or null
50105      */
50106     getActivePanel : function(){
50107         return this.activePanel;
50108     },
50109     
50110     /**
50111      * Add the passed ContentPanel(s)
50112      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50113      * @return {Roo.ContentPanel} The panel added (if only one was added)
50114      */
50115     add : function(panel){
50116         if(arguments.length > 1){
50117             for(var i = 0, len = arguments.length; i < len; i++) {
50118                 this.add(arguments[i]);
50119             }
50120             return null;
50121         }
50122         if(this.hasPanel(panel)){
50123             this.showPanel(panel);
50124             return panel;
50125         }
50126         var el = panel.getEl();
50127         if(el.dom.parentNode != this.mgr.el.dom){
50128             this.mgr.el.dom.appendChild(el.dom);
50129         }
50130         if(panel.setRegion){
50131             panel.setRegion(this);
50132         }
50133         this.panels.add(panel);
50134         el.setStyle("position", "absolute");
50135         if(!panel.background){
50136             this.setActivePanel(panel);
50137             if(this.config.initialSize && this.panels.getCount()==1){
50138                 this.resizeTo(this.config.initialSize);
50139             }
50140         }
50141         this.fireEvent("paneladded", this, panel);
50142         return panel;
50143     },
50144     
50145     /**
50146      * Returns true if the panel is in this region.
50147      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50148      * @return {Boolean}
50149      */
50150     hasPanel : function(panel){
50151         if(typeof panel == "object"){ // must be panel obj
50152             panel = panel.getId();
50153         }
50154         return this.getPanel(panel) ? true : false;
50155     },
50156     
50157     /**
50158      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50159      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50160      * @param {Boolean} preservePanel Overrides the config preservePanel option
50161      * @return {Roo.ContentPanel} The panel that was removed
50162      */
50163     remove : function(panel, preservePanel){
50164         panel = this.getPanel(panel);
50165         if(!panel){
50166             return null;
50167         }
50168         var e = {};
50169         this.fireEvent("beforeremove", this, panel, e);
50170         if(e.cancel === true){
50171             return null;
50172         }
50173         var panelId = panel.getId();
50174         this.panels.removeKey(panelId);
50175         return panel;
50176     },
50177     
50178     /**
50179      * Returns the panel specified or null if it's not in this region.
50180      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50181      * @return {Roo.ContentPanel}
50182      */
50183     getPanel : function(id){
50184         if(typeof id == "object"){ // must be panel obj
50185             return id;
50186         }
50187         return this.panels.get(id);
50188     },
50189     
50190     /**
50191      * Returns this regions position (north/south/east/west/center).
50192      * @return {String} 
50193      */
50194     getPosition: function(){
50195         return this.position;    
50196     }
50197 });/*
50198  * Based on:
50199  * Ext JS Library 1.1.1
50200  * Copyright(c) 2006-2007, Ext JS, LLC.
50201  *
50202  * Originally Released Under LGPL - original licence link has changed is not relivant.
50203  *
50204  * Fork - LGPL
50205  * <script type="text/javascript">
50206  */
50207  
50208 /**
50209  * @class Roo.LayoutRegion
50210  * @extends Roo.BasicLayoutRegion
50211  * This class represents a region in a layout manager.
50212  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50213  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50214  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50215  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50216  * @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})
50217  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50218  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50219  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50220  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50221  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50222  * @cfg {String}    title           The title for the region (overrides panel titles)
50223  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50224  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50225  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50226  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50227  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50228  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50229  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50230  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50231  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50232  * @cfg {Boolean}   showPin         True to show a pin button
50233  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50234  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50235  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50236  * @cfg {Number}    width           For East/West panels
50237  * @cfg {Number}    height          For North/South panels
50238  * @cfg {Boolean}   split           To show the splitter
50239  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50240  */
50241 Roo.LayoutRegion = function(mgr, config, pos){
50242     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50243     var dh = Roo.DomHelper;
50244     /** This region's container element 
50245     * @type Roo.Element */
50246     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50247     /** This region's title element 
50248     * @type Roo.Element */
50249
50250     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50251         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50252         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50253     ]}, true);
50254     this.titleEl.enableDisplayMode();
50255     /** This region's title text element 
50256     * @type HTMLElement */
50257     this.titleTextEl = this.titleEl.dom.firstChild;
50258     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50259     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50260     this.closeBtn.enableDisplayMode();
50261     this.closeBtn.on("click", this.closeClicked, this);
50262     this.closeBtn.hide();
50263
50264     this.createBody(config);
50265     this.visible = true;
50266     this.collapsed = false;
50267
50268     if(config.hideWhenEmpty){
50269         this.hide();
50270         this.on("paneladded", this.validateVisibility, this);
50271         this.on("panelremoved", this.validateVisibility, this);
50272     }
50273     this.applyConfig(config);
50274 };
50275
50276 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50277
50278     createBody : function(){
50279         /** This region's body element 
50280         * @type Roo.Element */
50281         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50282     },
50283
50284     applyConfig : function(c){
50285         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50286             var dh = Roo.DomHelper;
50287             if(c.titlebar !== false){
50288                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50289                 this.collapseBtn.on("click", this.collapse, this);
50290                 this.collapseBtn.enableDisplayMode();
50291
50292                 if(c.showPin === true || this.showPin){
50293                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50294                     this.stickBtn.enableDisplayMode();
50295                     this.stickBtn.on("click", this.expand, this);
50296                     this.stickBtn.hide();
50297                 }
50298             }
50299             /** This region's collapsed element
50300             * @type Roo.Element */
50301             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50302                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50303             ]}, true);
50304             if(c.floatable !== false){
50305                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50306                this.collapsedEl.on("click", this.collapseClick, this);
50307             }
50308
50309             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50310                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50311                    id: "message", unselectable: "on", style:{"float":"left"}});
50312                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50313              }
50314             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50315             this.expandBtn.on("click", this.expand, this);
50316         }
50317         if(this.collapseBtn){
50318             this.collapseBtn.setVisible(c.collapsible == true);
50319         }
50320         this.cmargins = c.cmargins || this.cmargins ||
50321                          (this.position == "west" || this.position == "east" ?
50322                              {top: 0, left: 2, right:2, bottom: 0} :
50323                              {top: 2, left: 0, right:0, bottom: 2});
50324         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50325         this.bottomTabs = c.tabPosition != "top";
50326         this.autoScroll = c.autoScroll || false;
50327         if(this.autoScroll){
50328             this.bodyEl.setStyle("overflow", "auto");
50329         }else{
50330             this.bodyEl.setStyle("overflow", "hidden");
50331         }
50332         //if(c.titlebar !== false){
50333             if((!c.titlebar && !c.title) || c.titlebar === false){
50334                 this.titleEl.hide();
50335             }else{
50336                 this.titleEl.show();
50337                 if(c.title){
50338                     this.titleTextEl.innerHTML = c.title;
50339                 }
50340             }
50341         //}
50342         this.duration = c.duration || .30;
50343         this.slideDuration = c.slideDuration || .45;
50344         this.config = c;
50345         if(c.collapsed){
50346             this.collapse(true);
50347         }
50348         if(c.hidden){
50349             this.hide();
50350         }
50351     },
50352     /**
50353      * Returns true if this region is currently visible.
50354      * @return {Boolean}
50355      */
50356     isVisible : function(){
50357         return this.visible;
50358     },
50359
50360     /**
50361      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50362      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50363      */
50364     setCollapsedTitle : function(title){
50365         title = title || "&#160;";
50366         if(this.collapsedTitleTextEl){
50367             this.collapsedTitleTextEl.innerHTML = title;
50368         }
50369     },
50370
50371     getBox : function(){
50372         var b;
50373         if(!this.collapsed){
50374             b = this.el.getBox(false, true);
50375         }else{
50376             b = this.collapsedEl.getBox(false, true);
50377         }
50378         return b;
50379     },
50380
50381     getMargins : function(){
50382         return this.collapsed ? this.cmargins : this.margins;
50383     },
50384
50385     highlight : function(){
50386         this.el.addClass("x-layout-panel-dragover");
50387     },
50388
50389     unhighlight : function(){
50390         this.el.removeClass("x-layout-panel-dragover");
50391     },
50392
50393     updateBox : function(box){
50394         this.box = box;
50395         if(!this.collapsed){
50396             this.el.dom.style.left = box.x + "px";
50397             this.el.dom.style.top = box.y + "px";
50398             this.updateBody(box.width, box.height);
50399         }else{
50400             this.collapsedEl.dom.style.left = box.x + "px";
50401             this.collapsedEl.dom.style.top = box.y + "px";
50402             this.collapsedEl.setSize(box.width, box.height);
50403         }
50404         if(this.tabs){
50405             this.tabs.autoSizeTabs();
50406         }
50407     },
50408
50409     updateBody : function(w, h){
50410         if(w !== null){
50411             this.el.setWidth(w);
50412             w -= this.el.getBorderWidth("rl");
50413             if(this.config.adjustments){
50414                 w += this.config.adjustments[0];
50415             }
50416         }
50417         if(h !== null){
50418             this.el.setHeight(h);
50419             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50420             h -= this.el.getBorderWidth("tb");
50421             if(this.config.adjustments){
50422                 h += this.config.adjustments[1];
50423             }
50424             this.bodyEl.setHeight(h);
50425             if(this.tabs){
50426                 h = this.tabs.syncHeight(h);
50427             }
50428         }
50429         if(this.panelSize){
50430             w = w !== null ? w : this.panelSize.width;
50431             h = h !== null ? h : this.panelSize.height;
50432         }
50433         if(this.activePanel){
50434             var el = this.activePanel.getEl();
50435             w = w !== null ? w : el.getWidth();
50436             h = h !== null ? h : el.getHeight();
50437             this.panelSize = {width: w, height: h};
50438             this.activePanel.setSize(w, h);
50439         }
50440         if(Roo.isIE && this.tabs){
50441             this.tabs.el.repaint();
50442         }
50443     },
50444
50445     /**
50446      * Returns the container element for this region.
50447      * @return {Roo.Element}
50448      */
50449     getEl : function(){
50450         return this.el;
50451     },
50452
50453     /**
50454      * Hides this region.
50455      */
50456     hide : function(){
50457         if(!this.collapsed){
50458             this.el.dom.style.left = "-2000px";
50459             this.el.hide();
50460         }else{
50461             this.collapsedEl.dom.style.left = "-2000px";
50462             this.collapsedEl.hide();
50463         }
50464         this.visible = false;
50465         this.fireEvent("visibilitychange", this, false);
50466     },
50467
50468     /**
50469      * Shows this region if it was previously hidden.
50470      */
50471     show : function(){
50472         if(!this.collapsed){
50473             this.el.show();
50474         }else{
50475             this.collapsedEl.show();
50476         }
50477         this.visible = true;
50478         this.fireEvent("visibilitychange", this, true);
50479     },
50480
50481     closeClicked : function(){
50482         if(this.activePanel){
50483             this.remove(this.activePanel);
50484         }
50485     },
50486
50487     collapseClick : function(e){
50488         if(this.isSlid){
50489            e.stopPropagation();
50490            this.slideIn();
50491         }else{
50492            e.stopPropagation();
50493            this.slideOut();
50494         }
50495     },
50496
50497     /**
50498      * Collapses this region.
50499      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50500      */
50501     collapse : function(skipAnim){
50502         if(this.collapsed) return;
50503         this.collapsed = true;
50504         if(this.split){
50505             this.split.el.hide();
50506         }
50507         if(this.config.animate && skipAnim !== true){
50508             this.fireEvent("invalidated", this);
50509             this.animateCollapse();
50510         }else{
50511             this.el.setLocation(-20000,-20000);
50512             this.el.hide();
50513             this.collapsedEl.show();
50514             this.fireEvent("collapsed", this);
50515             this.fireEvent("invalidated", this);
50516         }
50517     },
50518
50519     animateCollapse : function(){
50520         // overridden
50521     },
50522
50523     /**
50524      * Expands this region if it was previously collapsed.
50525      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50526      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50527      */
50528     expand : function(e, skipAnim){
50529         if(e) e.stopPropagation();
50530         if(!this.collapsed || this.el.hasActiveFx()) return;
50531         if(this.isSlid){
50532             this.afterSlideIn();
50533             skipAnim = true;
50534         }
50535         this.collapsed = false;
50536         if(this.config.animate && skipAnim !== true){
50537             this.animateExpand();
50538         }else{
50539             this.el.show();
50540             if(this.split){
50541                 this.split.el.show();
50542             }
50543             this.collapsedEl.setLocation(-2000,-2000);
50544             this.collapsedEl.hide();
50545             this.fireEvent("invalidated", this);
50546             this.fireEvent("expanded", this);
50547         }
50548     },
50549
50550     animateExpand : function(){
50551         // overridden
50552     },
50553
50554     initTabs : function()
50555     {
50556         this.bodyEl.setStyle("overflow", "hidden");
50557         var ts = new Roo.TabPanel(
50558                 this.bodyEl.dom,
50559                 {
50560                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50561                     disableTooltips: this.config.disableTabTips,
50562                     toolbar : this.config.toolbar
50563                 }
50564         );
50565         if(this.config.hideTabs){
50566             ts.stripWrap.setDisplayed(false);
50567         }
50568         this.tabs = ts;
50569         ts.resizeTabs = this.config.resizeTabs === true;
50570         ts.minTabWidth = this.config.minTabWidth || 40;
50571         ts.maxTabWidth = this.config.maxTabWidth || 250;
50572         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50573         ts.monitorResize = false;
50574         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50575         ts.bodyEl.addClass('x-layout-tabs-body');
50576         this.panels.each(this.initPanelAsTab, this);
50577     },
50578
50579     initPanelAsTab : function(panel){
50580         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50581                     this.config.closeOnTab && panel.isClosable());
50582         if(panel.tabTip !== undefined){
50583             ti.setTooltip(panel.tabTip);
50584         }
50585         ti.on("activate", function(){
50586               this.setActivePanel(panel);
50587         }, this);
50588         if(this.config.closeOnTab){
50589             ti.on("beforeclose", function(t, e){
50590                 e.cancel = true;
50591                 this.remove(panel);
50592             }, this);
50593         }
50594         return ti;
50595     },
50596
50597     updatePanelTitle : function(panel, title){
50598         if(this.activePanel == panel){
50599             this.updateTitle(title);
50600         }
50601         if(this.tabs){
50602             var ti = this.tabs.getTab(panel.getEl().id);
50603             ti.setText(title);
50604             if(panel.tabTip !== undefined){
50605                 ti.setTooltip(panel.tabTip);
50606             }
50607         }
50608     },
50609
50610     updateTitle : function(title){
50611         if(this.titleTextEl && !this.config.title){
50612             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50613         }
50614     },
50615
50616     setActivePanel : function(panel){
50617         panel = this.getPanel(panel);
50618         if(this.activePanel && this.activePanel != panel){
50619             this.activePanel.setActiveState(false);
50620         }
50621         this.activePanel = panel;
50622         panel.setActiveState(true);
50623         if(this.panelSize){
50624             panel.setSize(this.panelSize.width, this.panelSize.height);
50625         }
50626         if(this.closeBtn){
50627             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50628         }
50629         this.updateTitle(panel.getTitle());
50630         if(this.tabs){
50631             this.fireEvent("invalidated", this);
50632         }
50633         this.fireEvent("panelactivated", this, panel);
50634     },
50635
50636     /**
50637      * Shows the specified panel.
50638      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50639      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50640      */
50641     showPanel : function(panel)
50642     {
50643         panel = this.getPanel(panel);
50644         if(panel){
50645             if(this.tabs){
50646                 var tab = this.tabs.getTab(panel.getEl().id);
50647                 if(tab.isHidden()){
50648                     this.tabs.unhideTab(tab.id);
50649                 }
50650                 tab.activate();
50651             }else{
50652                 this.setActivePanel(panel);
50653             }
50654         }
50655         return panel;
50656     },
50657
50658     /**
50659      * Get the active panel for this region.
50660      * @return {Roo.ContentPanel} The active panel or null
50661      */
50662     getActivePanel : function(){
50663         return this.activePanel;
50664     },
50665
50666     validateVisibility : function(){
50667         if(this.panels.getCount() < 1){
50668             this.updateTitle("&#160;");
50669             this.closeBtn.hide();
50670             this.hide();
50671         }else{
50672             if(!this.isVisible()){
50673                 this.show();
50674             }
50675         }
50676     },
50677
50678     /**
50679      * Adds the passed ContentPanel(s) to this region.
50680      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50681      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50682      */
50683     add : function(panel){
50684         if(arguments.length > 1){
50685             for(var i = 0, len = arguments.length; i < len; i++) {
50686                 this.add(arguments[i]);
50687             }
50688             return null;
50689         }
50690         if(this.hasPanel(panel)){
50691             this.showPanel(panel);
50692             return panel;
50693         }
50694         panel.setRegion(this);
50695         this.panels.add(panel);
50696         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50697             this.bodyEl.dom.appendChild(panel.getEl().dom);
50698             if(panel.background !== true){
50699                 this.setActivePanel(panel);
50700             }
50701             this.fireEvent("paneladded", this, panel);
50702             return panel;
50703         }
50704         if(!this.tabs){
50705             this.initTabs();
50706         }else{
50707             this.initPanelAsTab(panel);
50708         }
50709         if(panel.background !== true){
50710             this.tabs.activate(panel.getEl().id);
50711         }
50712         this.fireEvent("paneladded", this, panel);
50713         return panel;
50714     },
50715
50716     /**
50717      * Hides the tab for the specified panel.
50718      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50719      */
50720     hidePanel : function(panel){
50721         if(this.tabs && (panel = this.getPanel(panel))){
50722             this.tabs.hideTab(panel.getEl().id);
50723         }
50724     },
50725
50726     /**
50727      * Unhides the tab for a previously hidden panel.
50728      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50729      */
50730     unhidePanel : function(panel){
50731         if(this.tabs && (panel = this.getPanel(panel))){
50732             this.tabs.unhideTab(panel.getEl().id);
50733         }
50734     },
50735
50736     clearPanels : function(){
50737         while(this.panels.getCount() > 0){
50738              this.remove(this.panels.first());
50739         }
50740     },
50741
50742     /**
50743      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50744      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50745      * @param {Boolean} preservePanel Overrides the config preservePanel option
50746      * @return {Roo.ContentPanel} The panel that was removed
50747      */
50748     remove : function(panel, preservePanel){
50749         panel = this.getPanel(panel);
50750         if(!panel){
50751             return null;
50752         }
50753         var e = {};
50754         this.fireEvent("beforeremove", this, panel, e);
50755         if(e.cancel === true){
50756             return null;
50757         }
50758         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50759         var panelId = panel.getId();
50760         this.panels.removeKey(panelId);
50761         if(preservePanel){
50762             document.body.appendChild(panel.getEl().dom);
50763         }
50764         if(this.tabs){
50765             this.tabs.removeTab(panel.getEl().id);
50766         }else if (!preservePanel){
50767             this.bodyEl.dom.removeChild(panel.getEl().dom);
50768         }
50769         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50770             var p = this.panels.first();
50771             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50772             tempEl.appendChild(p.getEl().dom);
50773             this.bodyEl.update("");
50774             this.bodyEl.dom.appendChild(p.getEl().dom);
50775             tempEl = null;
50776             this.updateTitle(p.getTitle());
50777             this.tabs = null;
50778             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50779             this.setActivePanel(p);
50780         }
50781         panel.setRegion(null);
50782         if(this.activePanel == panel){
50783             this.activePanel = null;
50784         }
50785         if(this.config.autoDestroy !== false && preservePanel !== true){
50786             try{panel.destroy();}catch(e){}
50787         }
50788         this.fireEvent("panelremoved", this, panel);
50789         return panel;
50790     },
50791
50792     /**
50793      * Returns the TabPanel component used by this region
50794      * @return {Roo.TabPanel}
50795      */
50796     getTabs : function(){
50797         return this.tabs;
50798     },
50799
50800     createTool : function(parentEl, className){
50801         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50802             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50803         btn.addClassOnOver("x-layout-tools-button-over");
50804         return btn;
50805     }
50806 });/*
50807  * Based on:
50808  * Ext JS Library 1.1.1
50809  * Copyright(c) 2006-2007, Ext JS, LLC.
50810  *
50811  * Originally Released Under LGPL - original licence link has changed is not relivant.
50812  *
50813  * Fork - LGPL
50814  * <script type="text/javascript">
50815  */
50816  
50817
50818
50819 /**
50820  * @class Roo.SplitLayoutRegion
50821  * @extends Roo.LayoutRegion
50822  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50823  */
50824 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50825     this.cursor = cursor;
50826     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50827 };
50828
50829 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50830     splitTip : "Drag to resize.",
50831     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50832     useSplitTips : false,
50833
50834     applyConfig : function(config){
50835         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50836         if(config.split){
50837             if(!this.split){
50838                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50839                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50840                 /** The SplitBar for this region 
50841                 * @type Roo.SplitBar */
50842                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50843                 this.split.on("moved", this.onSplitMove, this);
50844                 this.split.useShim = config.useShim === true;
50845                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50846                 if(this.useSplitTips){
50847                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50848                 }
50849                 if(config.collapsible){
50850                     this.split.el.on("dblclick", this.collapse,  this);
50851                 }
50852             }
50853             if(typeof config.minSize != "undefined"){
50854                 this.split.minSize = config.minSize;
50855             }
50856             if(typeof config.maxSize != "undefined"){
50857                 this.split.maxSize = config.maxSize;
50858             }
50859             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50860                 this.hideSplitter();
50861             }
50862         }
50863     },
50864
50865     getHMaxSize : function(){
50866          var cmax = this.config.maxSize || 10000;
50867          var center = this.mgr.getRegion("center");
50868          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50869     },
50870
50871     getVMaxSize : function(){
50872          var cmax = this.config.maxSize || 10000;
50873          var center = this.mgr.getRegion("center");
50874          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50875     },
50876
50877     onSplitMove : function(split, newSize){
50878         this.fireEvent("resized", this, newSize);
50879     },
50880     
50881     /** 
50882      * Returns the {@link Roo.SplitBar} for this region.
50883      * @return {Roo.SplitBar}
50884      */
50885     getSplitBar : function(){
50886         return this.split;
50887     },
50888     
50889     hide : function(){
50890         this.hideSplitter();
50891         Roo.SplitLayoutRegion.superclass.hide.call(this);
50892     },
50893
50894     hideSplitter : function(){
50895         if(this.split){
50896             this.split.el.setLocation(-2000,-2000);
50897             this.split.el.hide();
50898         }
50899     },
50900
50901     show : function(){
50902         if(this.split){
50903             this.split.el.show();
50904         }
50905         Roo.SplitLayoutRegion.superclass.show.call(this);
50906     },
50907     
50908     beforeSlide: function(){
50909         if(Roo.isGecko){// firefox overflow auto bug workaround
50910             this.bodyEl.clip();
50911             if(this.tabs) this.tabs.bodyEl.clip();
50912             if(this.activePanel){
50913                 this.activePanel.getEl().clip();
50914                 
50915                 if(this.activePanel.beforeSlide){
50916                     this.activePanel.beforeSlide();
50917                 }
50918             }
50919         }
50920     },
50921     
50922     afterSlide : function(){
50923         if(Roo.isGecko){// firefox overflow auto bug workaround
50924             this.bodyEl.unclip();
50925             if(this.tabs) this.tabs.bodyEl.unclip();
50926             if(this.activePanel){
50927                 this.activePanel.getEl().unclip();
50928                 if(this.activePanel.afterSlide){
50929                     this.activePanel.afterSlide();
50930                 }
50931             }
50932         }
50933     },
50934
50935     initAutoHide : function(){
50936         if(this.autoHide !== false){
50937             if(!this.autoHideHd){
50938                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50939                 this.autoHideHd = {
50940                     "mouseout": function(e){
50941                         if(!e.within(this.el, true)){
50942                             st.delay(500);
50943                         }
50944                     },
50945                     "mouseover" : function(e){
50946                         st.cancel();
50947                     },
50948                     scope : this
50949                 };
50950             }
50951             this.el.on(this.autoHideHd);
50952         }
50953     },
50954
50955     clearAutoHide : function(){
50956         if(this.autoHide !== false){
50957             this.el.un("mouseout", this.autoHideHd.mouseout);
50958             this.el.un("mouseover", this.autoHideHd.mouseover);
50959         }
50960     },
50961
50962     clearMonitor : function(){
50963         Roo.get(document).un("click", this.slideInIf, this);
50964     },
50965
50966     // these names are backwards but not changed for compat
50967     slideOut : function(){
50968         if(this.isSlid || this.el.hasActiveFx()){
50969             return;
50970         }
50971         this.isSlid = true;
50972         if(this.collapseBtn){
50973             this.collapseBtn.hide();
50974         }
50975         this.closeBtnState = this.closeBtn.getStyle('display');
50976         this.closeBtn.hide();
50977         if(this.stickBtn){
50978             this.stickBtn.show();
50979         }
50980         this.el.show();
50981         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50982         this.beforeSlide();
50983         this.el.setStyle("z-index", 10001);
50984         this.el.slideIn(this.getSlideAnchor(), {
50985             callback: function(){
50986                 this.afterSlide();
50987                 this.initAutoHide();
50988                 Roo.get(document).on("click", this.slideInIf, this);
50989                 this.fireEvent("slideshow", this);
50990             },
50991             scope: this,
50992             block: true
50993         });
50994     },
50995
50996     afterSlideIn : function(){
50997         this.clearAutoHide();
50998         this.isSlid = false;
50999         this.clearMonitor();
51000         this.el.setStyle("z-index", "");
51001         if(this.collapseBtn){
51002             this.collapseBtn.show();
51003         }
51004         this.closeBtn.setStyle('display', this.closeBtnState);
51005         if(this.stickBtn){
51006             this.stickBtn.hide();
51007         }
51008         this.fireEvent("slidehide", this);
51009     },
51010
51011     slideIn : function(cb){
51012         if(!this.isSlid || this.el.hasActiveFx()){
51013             Roo.callback(cb);
51014             return;
51015         }
51016         this.isSlid = false;
51017         this.beforeSlide();
51018         this.el.slideOut(this.getSlideAnchor(), {
51019             callback: function(){
51020                 this.el.setLeftTop(-10000, -10000);
51021                 this.afterSlide();
51022                 this.afterSlideIn();
51023                 Roo.callback(cb);
51024             },
51025             scope: this,
51026             block: true
51027         });
51028     },
51029     
51030     slideInIf : function(e){
51031         if(!e.within(this.el)){
51032             this.slideIn();
51033         }
51034     },
51035
51036     animateCollapse : function(){
51037         this.beforeSlide();
51038         this.el.setStyle("z-index", 20000);
51039         var anchor = this.getSlideAnchor();
51040         this.el.slideOut(anchor, {
51041             callback : function(){
51042                 this.el.setStyle("z-index", "");
51043                 this.collapsedEl.slideIn(anchor, {duration:.3});
51044                 this.afterSlide();
51045                 this.el.setLocation(-10000,-10000);
51046                 this.el.hide();
51047                 this.fireEvent("collapsed", this);
51048             },
51049             scope: this,
51050             block: true
51051         });
51052     },
51053
51054     animateExpand : function(){
51055         this.beforeSlide();
51056         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51057         this.el.setStyle("z-index", 20000);
51058         this.collapsedEl.hide({
51059             duration:.1
51060         });
51061         this.el.slideIn(this.getSlideAnchor(), {
51062             callback : function(){
51063                 this.el.setStyle("z-index", "");
51064                 this.afterSlide();
51065                 if(this.split){
51066                     this.split.el.show();
51067                 }
51068                 this.fireEvent("invalidated", this);
51069                 this.fireEvent("expanded", this);
51070             },
51071             scope: this,
51072             block: true
51073         });
51074     },
51075
51076     anchors : {
51077         "west" : "left",
51078         "east" : "right",
51079         "north" : "top",
51080         "south" : "bottom"
51081     },
51082
51083     sanchors : {
51084         "west" : "l",
51085         "east" : "r",
51086         "north" : "t",
51087         "south" : "b"
51088     },
51089
51090     canchors : {
51091         "west" : "tl-tr",
51092         "east" : "tr-tl",
51093         "north" : "tl-bl",
51094         "south" : "bl-tl"
51095     },
51096
51097     getAnchor : function(){
51098         return this.anchors[this.position];
51099     },
51100
51101     getCollapseAnchor : function(){
51102         return this.canchors[this.position];
51103     },
51104
51105     getSlideAnchor : function(){
51106         return this.sanchors[this.position];
51107     },
51108
51109     getAlignAdj : function(){
51110         var cm = this.cmargins;
51111         switch(this.position){
51112             case "west":
51113                 return [0, 0];
51114             break;
51115             case "east":
51116                 return [0, 0];
51117             break;
51118             case "north":
51119                 return [0, 0];
51120             break;
51121             case "south":
51122                 return [0, 0];
51123             break;
51124         }
51125     },
51126
51127     getExpandAdj : function(){
51128         var c = this.collapsedEl, cm = this.cmargins;
51129         switch(this.position){
51130             case "west":
51131                 return [-(cm.right+c.getWidth()+cm.left), 0];
51132             break;
51133             case "east":
51134                 return [cm.right+c.getWidth()+cm.left, 0];
51135             break;
51136             case "north":
51137                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51138             break;
51139             case "south":
51140                 return [0, cm.top+cm.bottom+c.getHeight()];
51141             break;
51142         }
51143     }
51144 });/*
51145  * Based on:
51146  * Ext JS Library 1.1.1
51147  * Copyright(c) 2006-2007, Ext JS, LLC.
51148  *
51149  * Originally Released Under LGPL - original licence link has changed is not relivant.
51150  *
51151  * Fork - LGPL
51152  * <script type="text/javascript">
51153  */
51154 /*
51155  * These classes are private internal classes
51156  */
51157 Roo.CenterLayoutRegion = function(mgr, config){
51158     Roo.LayoutRegion.call(this, mgr, config, "center");
51159     this.visible = true;
51160     this.minWidth = config.minWidth || 20;
51161     this.minHeight = config.minHeight || 20;
51162 };
51163
51164 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51165     hide : function(){
51166         // center panel can't be hidden
51167     },
51168     
51169     show : function(){
51170         // center panel can't be hidden
51171     },
51172     
51173     getMinWidth: function(){
51174         return this.minWidth;
51175     },
51176     
51177     getMinHeight: function(){
51178         return this.minHeight;
51179     }
51180 });
51181
51182
51183 Roo.NorthLayoutRegion = function(mgr, config){
51184     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51185     if(this.split){
51186         this.split.placement = Roo.SplitBar.TOP;
51187         this.split.orientation = Roo.SplitBar.VERTICAL;
51188         this.split.el.addClass("x-layout-split-v");
51189     }
51190     var size = config.initialSize || config.height;
51191     if(typeof size != "undefined"){
51192         this.el.setHeight(size);
51193     }
51194 };
51195 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51196     orientation: Roo.SplitBar.VERTICAL,
51197     getBox : function(){
51198         if(this.collapsed){
51199             return this.collapsedEl.getBox();
51200         }
51201         var box = this.el.getBox();
51202         if(this.split){
51203             box.height += this.split.el.getHeight();
51204         }
51205         return box;
51206     },
51207     
51208     updateBox : function(box){
51209         if(this.split && !this.collapsed){
51210             box.height -= this.split.el.getHeight();
51211             this.split.el.setLeft(box.x);
51212             this.split.el.setTop(box.y+box.height);
51213             this.split.el.setWidth(box.width);
51214         }
51215         if(this.collapsed){
51216             this.updateBody(box.width, null);
51217         }
51218         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51219     }
51220 });
51221
51222 Roo.SouthLayoutRegion = function(mgr, config){
51223     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51224     if(this.split){
51225         this.split.placement = Roo.SplitBar.BOTTOM;
51226         this.split.orientation = Roo.SplitBar.VERTICAL;
51227         this.split.el.addClass("x-layout-split-v");
51228     }
51229     var size = config.initialSize || config.height;
51230     if(typeof size != "undefined"){
51231         this.el.setHeight(size);
51232     }
51233 };
51234 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51235     orientation: Roo.SplitBar.VERTICAL,
51236     getBox : function(){
51237         if(this.collapsed){
51238             return this.collapsedEl.getBox();
51239         }
51240         var box = this.el.getBox();
51241         if(this.split){
51242             var sh = this.split.el.getHeight();
51243             box.height += sh;
51244             box.y -= sh;
51245         }
51246         return box;
51247     },
51248     
51249     updateBox : function(box){
51250         if(this.split && !this.collapsed){
51251             var sh = this.split.el.getHeight();
51252             box.height -= sh;
51253             box.y += sh;
51254             this.split.el.setLeft(box.x);
51255             this.split.el.setTop(box.y-sh);
51256             this.split.el.setWidth(box.width);
51257         }
51258         if(this.collapsed){
51259             this.updateBody(box.width, null);
51260         }
51261         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51262     }
51263 });
51264
51265 Roo.EastLayoutRegion = function(mgr, config){
51266     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51267     if(this.split){
51268         this.split.placement = Roo.SplitBar.RIGHT;
51269         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51270         this.split.el.addClass("x-layout-split-h");
51271     }
51272     var size = config.initialSize || config.width;
51273     if(typeof size != "undefined"){
51274         this.el.setWidth(size);
51275     }
51276 };
51277 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51278     orientation: Roo.SplitBar.HORIZONTAL,
51279     getBox : function(){
51280         if(this.collapsed){
51281             return this.collapsedEl.getBox();
51282         }
51283         var box = this.el.getBox();
51284         if(this.split){
51285             var sw = this.split.el.getWidth();
51286             box.width += sw;
51287             box.x -= sw;
51288         }
51289         return box;
51290     },
51291
51292     updateBox : function(box){
51293         if(this.split && !this.collapsed){
51294             var sw = this.split.el.getWidth();
51295             box.width -= sw;
51296             this.split.el.setLeft(box.x);
51297             this.split.el.setTop(box.y);
51298             this.split.el.setHeight(box.height);
51299             box.x += sw;
51300         }
51301         if(this.collapsed){
51302             this.updateBody(null, box.height);
51303         }
51304         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51305     }
51306 });
51307
51308 Roo.WestLayoutRegion = function(mgr, config){
51309     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51310     if(this.split){
51311         this.split.placement = Roo.SplitBar.LEFT;
51312         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51313         this.split.el.addClass("x-layout-split-h");
51314     }
51315     var size = config.initialSize || config.width;
51316     if(typeof size != "undefined"){
51317         this.el.setWidth(size);
51318     }
51319 };
51320 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51321     orientation: Roo.SplitBar.HORIZONTAL,
51322     getBox : function(){
51323         if(this.collapsed){
51324             return this.collapsedEl.getBox();
51325         }
51326         var box = this.el.getBox();
51327         if(this.split){
51328             box.width += this.split.el.getWidth();
51329         }
51330         return box;
51331     },
51332     
51333     updateBox : function(box){
51334         if(this.split && !this.collapsed){
51335             var sw = this.split.el.getWidth();
51336             box.width -= sw;
51337             this.split.el.setLeft(box.x+box.width);
51338             this.split.el.setTop(box.y);
51339             this.split.el.setHeight(box.height);
51340         }
51341         if(this.collapsed){
51342             this.updateBody(null, box.height);
51343         }
51344         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51345     }
51346 });
51347 /*
51348  * Based on:
51349  * Ext JS Library 1.1.1
51350  * Copyright(c) 2006-2007, Ext JS, LLC.
51351  *
51352  * Originally Released Under LGPL - original licence link has changed is not relivant.
51353  *
51354  * Fork - LGPL
51355  * <script type="text/javascript">
51356  */
51357  
51358  
51359 /*
51360  * Private internal class for reading and applying state
51361  */
51362 Roo.LayoutStateManager = function(layout){
51363      // default empty state
51364      this.state = {
51365         north: {},
51366         south: {},
51367         east: {},
51368         west: {}       
51369     };
51370 };
51371
51372 Roo.LayoutStateManager.prototype = {
51373     init : function(layout, provider){
51374         this.provider = provider;
51375         var state = provider.get(layout.id+"-layout-state");
51376         if(state){
51377             var wasUpdating = layout.isUpdating();
51378             if(!wasUpdating){
51379                 layout.beginUpdate();
51380             }
51381             for(var key in state){
51382                 if(typeof state[key] != "function"){
51383                     var rstate = state[key];
51384                     var r = layout.getRegion(key);
51385                     if(r && rstate){
51386                         if(rstate.size){
51387                             r.resizeTo(rstate.size);
51388                         }
51389                         if(rstate.collapsed == true){
51390                             r.collapse(true);
51391                         }else{
51392                             r.expand(null, true);
51393                         }
51394                     }
51395                 }
51396             }
51397             if(!wasUpdating){
51398                 layout.endUpdate();
51399             }
51400             this.state = state; 
51401         }
51402         this.layout = layout;
51403         layout.on("regionresized", this.onRegionResized, this);
51404         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51405         layout.on("regionexpanded", this.onRegionExpanded, this);
51406     },
51407     
51408     storeState : function(){
51409         this.provider.set(this.layout.id+"-layout-state", this.state);
51410     },
51411     
51412     onRegionResized : function(region, newSize){
51413         this.state[region.getPosition()].size = newSize;
51414         this.storeState();
51415     },
51416     
51417     onRegionCollapsed : function(region){
51418         this.state[region.getPosition()].collapsed = true;
51419         this.storeState();
51420     },
51421     
51422     onRegionExpanded : function(region){
51423         this.state[region.getPosition()].collapsed = false;
51424         this.storeState();
51425     }
51426 };/*
51427  * Based on:
51428  * Ext JS Library 1.1.1
51429  * Copyright(c) 2006-2007, Ext JS, LLC.
51430  *
51431  * Originally Released Under LGPL - original licence link has changed is not relivant.
51432  *
51433  * Fork - LGPL
51434  * <script type="text/javascript">
51435  */
51436 /**
51437  * @class Roo.ContentPanel
51438  * @extends Roo.util.Observable
51439  * A basic ContentPanel element.
51440  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51441  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51442  * @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
51443  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51444  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51445  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51446  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51447  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51448  * @cfg {String} title          The title for this panel
51449  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51450  * @cfg {String} url            Calls {@link #setUrl} with this value
51451  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51452  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51453  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51454  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51455
51456  * @constructor
51457  * Create a new ContentPanel.
51458  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51459  * @param {String/Object} config A string to set only the title or a config object
51460  * @param {String} content (optional) Set the HTML content for this panel
51461  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51462  */
51463 Roo.ContentPanel = function(el, config, content){
51464     
51465      
51466     /*
51467     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51468         config = el;
51469         el = Roo.id();
51470     }
51471     if (config && config.parentLayout) { 
51472         el = config.parentLayout.el.createChild(); 
51473     }
51474     */
51475     if(el.autoCreate){ // xtype is available if this is called from factory
51476         config = el;
51477         el = Roo.id();
51478     }
51479     this.el = Roo.get(el);
51480     if(!this.el && config && config.autoCreate){
51481         if(typeof config.autoCreate == "object"){
51482             if(!config.autoCreate.id){
51483                 config.autoCreate.id = config.id||el;
51484             }
51485             this.el = Roo.DomHelper.append(document.body,
51486                         config.autoCreate, true);
51487         }else{
51488             this.el = Roo.DomHelper.append(document.body,
51489                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51490         }
51491     }
51492     this.closable = false;
51493     this.loaded = false;
51494     this.active = false;
51495     if(typeof config == "string"){
51496         this.title = config;
51497     }else{
51498         Roo.apply(this, config);
51499     }
51500     
51501     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51502         this.wrapEl = this.el.wrap();
51503         this.toolbar.container = this.el.insertSibling(false, 'before');
51504         this.toolbar = new Roo.Toolbar(this.toolbar);
51505     }
51506     
51507     // xtype created footer. - not sure if will work as we normally have to render first..
51508     if (this.footer && !this.footer.el && this.footer.xtype) {
51509         if (!this.wrapEl) {
51510             this.wrapEl = this.el.wrap();
51511         }
51512     
51513         this.footer.container = this.wrapEl.createChild();
51514          
51515         this.footer = Roo.factory(this.footer, Roo);
51516         
51517     }
51518     
51519     if(this.resizeEl){
51520         this.resizeEl = Roo.get(this.resizeEl, true);
51521     }else{
51522         this.resizeEl = this.el;
51523     }
51524     // handle view.xtype
51525     
51526  
51527     
51528     
51529     this.addEvents({
51530         /**
51531          * @event activate
51532          * Fires when this panel is activated. 
51533          * @param {Roo.ContentPanel} this
51534          */
51535         "activate" : true,
51536         /**
51537          * @event deactivate
51538          * Fires when this panel is activated. 
51539          * @param {Roo.ContentPanel} this
51540          */
51541         "deactivate" : true,
51542
51543         /**
51544          * @event resize
51545          * Fires when this panel is resized if fitToFrame is true.
51546          * @param {Roo.ContentPanel} this
51547          * @param {Number} width The width after any component adjustments
51548          * @param {Number} height The height after any component adjustments
51549          */
51550         "resize" : true,
51551         
51552          /**
51553          * @event render
51554          * Fires when this tab is created
51555          * @param {Roo.ContentPanel} this
51556          */
51557         "render" : true
51558         
51559         
51560         
51561     });
51562     
51563
51564     
51565     
51566     if(this.autoScroll){
51567         this.resizeEl.setStyle("overflow", "auto");
51568     } else {
51569         // fix randome scrolling
51570         this.el.on('scroll', function() {
51571             Roo.log('fix random scolling');
51572             this.scrollTo('top',0); 
51573         });
51574     }
51575     content = content || this.content;
51576     if(content){
51577         this.setContent(content);
51578     }
51579     if(config && config.url){
51580         this.setUrl(this.url, this.params, this.loadOnce);
51581     }
51582     
51583     
51584     
51585     Roo.ContentPanel.superclass.constructor.call(this);
51586     
51587     if (this.view && typeof(this.view.xtype) != 'undefined') {
51588         this.view.el = this.el.appendChild(document.createElement("div"));
51589         this.view = Roo.factory(this.view); 
51590         this.view.render  &&  this.view.render(false, '');  
51591     }
51592     
51593     
51594     this.fireEvent('render', this);
51595 };
51596
51597 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51598     tabTip:'',
51599     setRegion : function(region){
51600         this.region = region;
51601         if(region){
51602            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51603         }else{
51604            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51605         } 
51606     },
51607     
51608     /**
51609      * Returns the toolbar for this Panel if one was configured. 
51610      * @return {Roo.Toolbar} 
51611      */
51612     getToolbar : function(){
51613         return this.toolbar;
51614     },
51615     
51616     setActiveState : function(active){
51617         this.active = active;
51618         if(!active){
51619             this.fireEvent("deactivate", this);
51620         }else{
51621             this.fireEvent("activate", this);
51622         }
51623     },
51624     /**
51625      * Updates this panel's element
51626      * @param {String} content The new content
51627      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51628     */
51629     setContent : function(content, loadScripts){
51630         this.el.update(content, loadScripts);
51631     },
51632
51633     ignoreResize : function(w, h){
51634         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51635             return true;
51636         }else{
51637             this.lastSize = {width: w, height: h};
51638             return false;
51639         }
51640     },
51641     /**
51642      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51643      * @return {Roo.UpdateManager} The UpdateManager
51644      */
51645     getUpdateManager : function(){
51646         return this.el.getUpdateManager();
51647     },
51648      /**
51649      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51650      * @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:
51651 <pre><code>
51652 panel.load({
51653     url: "your-url.php",
51654     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51655     callback: yourFunction,
51656     scope: yourObject, //(optional scope)
51657     discardUrl: false,
51658     nocache: false,
51659     text: "Loading...",
51660     timeout: 30,
51661     scripts: false
51662 });
51663 </code></pre>
51664      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51665      * 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.
51666      * @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}
51667      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51668      * @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.
51669      * @return {Roo.ContentPanel} this
51670      */
51671     load : function(){
51672         var um = this.el.getUpdateManager();
51673         um.update.apply(um, arguments);
51674         return this;
51675     },
51676
51677
51678     /**
51679      * 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.
51680      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51681      * @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)
51682      * @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)
51683      * @return {Roo.UpdateManager} The UpdateManager
51684      */
51685     setUrl : function(url, params, loadOnce){
51686         if(this.refreshDelegate){
51687             this.removeListener("activate", this.refreshDelegate);
51688         }
51689         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51690         this.on("activate", this.refreshDelegate);
51691         return this.el.getUpdateManager();
51692     },
51693     
51694     _handleRefresh : function(url, params, loadOnce){
51695         if(!loadOnce || !this.loaded){
51696             var updater = this.el.getUpdateManager();
51697             updater.update(url, params, this._setLoaded.createDelegate(this));
51698         }
51699     },
51700     
51701     _setLoaded : function(){
51702         this.loaded = true;
51703     }, 
51704     
51705     /**
51706      * Returns this panel's id
51707      * @return {String} 
51708      */
51709     getId : function(){
51710         return this.el.id;
51711     },
51712     
51713     /** 
51714      * Returns this panel's element - used by regiosn to add.
51715      * @return {Roo.Element} 
51716      */
51717     getEl : function(){
51718         return this.wrapEl || this.el;
51719     },
51720     
51721     adjustForComponents : function(width, height)
51722     {
51723         //Roo.log('adjustForComponents ');
51724         if(this.resizeEl != this.el){
51725             width -= this.el.getFrameWidth('lr');
51726             height -= this.el.getFrameWidth('tb');
51727         }
51728         if(this.toolbar){
51729             var te = this.toolbar.getEl();
51730             height -= te.getHeight();
51731             te.setWidth(width);
51732         }
51733         if(this.footer){
51734             var te = this.footer.getEl();
51735             Roo.log("footer:" + te.getHeight());
51736             
51737             height -= te.getHeight();
51738             te.setWidth(width);
51739         }
51740         
51741         
51742         if(this.adjustments){
51743             width += this.adjustments[0];
51744             height += this.adjustments[1];
51745         }
51746         return {"width": width, "height": height};
51747     },
51748     
51749     setSize : function(width, height){
51750         if(this.fitToFrame && !this.ignoreResize(width, height)){
51751             if(this.fitContainer && this.resizeEl != this.el){
51752                 this.el.setSize(width, height);
51753             }
51754             var size = this.adjustForComponents(width, height);
51755             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51756             this.fireEvent('resize', this, size.width, size.height);
51757         }
51758     },
51759     
51760     /**
51761      * Returns this panel's title
51762      * @return {String} 
51763      */
51764     getTitle : function(){
51765         return this.title;
51766     },
51767     
51768     /**
51769      * Set this panel's title
51770      * @param {String} title
51771      */
51772     setTitle : function(title){
51773         this.title = title;
51774         if(this.region){
51775             this.region.updatePanelTitle(this, title);
51776         }
51777     },
51778     
51779     /**
51780      * Returns true is this panel was configured to be closable
51781      * @return {Boolean} 
51782      */
51783     isClosable : function(){
51784         return this.closable;
51785     },
51786     
51787     beforeSlide : function(){
51788         this.el.clip();
51789         this.resizeEl.clip();
51790     },
51791     
51792     afterSlide : function(){
51793         this.el.unclip();
51794         this.resizeEl.unclip();
51795     },
51796     
51797     /**
51798      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51799      *   Will fail silently if the {@link #setUrl} method has not been called.
51800      *   This does not activate the panel, just updates its content.
51801      */
51802     refresh : function(){
51803         if(this.refreshDelegate){
51804            this.loaded = false;
51805            this.refreshDelegate();
51806         }
51807     },
51808     
51809     /**
51810      * Destroys this panel
51811      */
51812     destroy : function(){
51813         this.el.removeAllListeners();
51814         var tempEl = document.createElement("span");
51815         tempEl.appendChild(this.el.dom);
51816         tempEl.innerHTML = "";
51817         this.el.remove();
51818         this.el = null;
51819     },
51820     
51821     /**
51822      * form - if the content panel contains a form - this is a reference to it.
51823      * @type {Roo.form.Form}
51824      */
51825     form : false,
51826     /**
51827      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51828      *    This contains a reference to it.
51829      * @type {Roo.View}
51830      */
51831     view : false,
51832     
51833       /**
51834      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51835      * <pre><code>
51836
51837 layout.addxtype({
51838        xtype : 'Form',
51839        items: [ .... ]
51840    }
51841 );
51842
51843 </code></pre>
51844      * @param {Object} cfg Xtype definition of item to add.
51845      */
51846     
51847     addxtype : function(cfg) {
51848         // add form..
51849         if (cfg.xtype.match(/^Form$/)) {
51850             
51851             var el;
51852             //if (this.footer) {
51853             //    el = this.footer.container.insertSibling(false, 'before');
51854             //} else {
51855                 el = this.el.createChild();
51856             //}
51857
51858             this.form = new  Roo.form.Form(cfg);
51859             
51860             
51861             if ( this.form.allItems.length) this.form.render(el.dom);
51862             return this.form;
51863         }
51864         // should only have one of theses..
51865         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51866             // views.. should not be just added - used named prop 'view''
51867             
51868             cfg.el = this.el.appendChild(document.createElement("div"));
51869             // factory?
51870             
51871             var ret = new Roo.factory(cfg);
51872              
51873              ret.render && ret.render(false, ''); // render blank..
51874             this.view = ret;
51875             return ret;
51876         }
51877         return false;
51878     }
51879 });
51880
51881 /**
51882  * @class Roo.GridPanel
51883  * @extends Roo.ContentPanel
51884  * @constructor
51885  * Create a new GridPanel.
51886  * @param {Roo.grid.Grid} grid The grid for this panel
51887  * @param {String/Object} config A string to set only the panel's title, or a config object
51888  */
51889 Roo.GridPanel = function(grid, config){
51890     
51891   
51892     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51893         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51894         
51895     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51896     
51897     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51898     
51899     if(this.toolbar){
51900         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51901     }
51902     // xtype created footer. - not sure if will work as we normally have to render first..
51903     if (this.footer && !this.footer.el && this.footer.xtype) {
51904         
51905         this.footer.container = this.grid.getView().getFooterPanel(true);
51906         this.footer.dataSource = this.grid.dataSource;
51907         this.footer = Roo.factory(this.footer, Roo);
51908         
51909     }
51910     
51911     grid.monitorWindowResize = false; // turn off autosizing
51912     grid.autoHeight = false;
51913     grid.autoWidth = false;
51914     this.grid = grid;
51915     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51916 };
51917
51918 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51919     getId : function(){
51920         return this.grid.id;
51921     },
51922     
51923     /**
51924      * Returns the grid for this panel
51925      * @return {Roo.grid.Grid} 
51926      */
51927     getGrid : function(){
51928         return this.grid;    
51929     },
51930     
51931     setSize : function(width, height){
51932         if(!this.ignoreResize(width, height)){
51933             var grid = this.grid;
51934             var size = this.adjustForComponents(width, height);
51935             grid.getGridEl().setSize(size.width, size.height);
51936             grid.autoSize();
51937         }
51938     },
51939     
51940     beforeSlide : function(){
51941         this.grid.getView().scroller.clip();
51942     },
51943     
51944     afterSlide : function(){
51945         this.grid.getView().scroller.unclip();
51946     },
51947     
51948     destroy : function(){
51949         this.grid.destroy();
51950         delete this.grid;
51951         Roo.GridPanel.superclass.destroy.call(this); 
51952     }
51953 });
51954
51955
51956 /**
51957  * @class Roo.NestedLayoutPanel
51958  * @extends Roo.ContentPanel
51959  * @constructor
51960  * Create a new NestedLayoutPanel.
51961  * 
51962  * 
51963  * @param {Roo.BorderLayout} layout The layout for this panel
51964  * @param {String/Object} config A string to set only the title or a config object
51965  */
51966 Roo.NestedLayoutPanel = function(layout, config)
51967 {
51968     // construct with only one argument..
51969     /* FIXME - implement nicer consturctors
51970     if (layout.layout) {
51971         config = layout;
51972         layout = config.layout;
51973         delete config.layout;
51974     }
51975     if (layout.xtype && !layout.getEl) {
51976         // then layout needs constructing..
51977         layout = Roo.factory(layout, Roo);
51978     }
51979     */
51980     
51981     
51982     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51983     
51984     layout.monitorWindowResize = false; // turn off autosizing
51985     this.layout = layout;
51986     this.layout.getEl().addClass("x-layout-nested-layout");
51987     
51988     
51989     
51990     
51991 };
51992
51993 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51994
51995     setSize : function(width, height){
51996         if(!this.ignoreResize(width, height)){
51997             var size = this.adjustForComponents(width, height);
51998             var el = this.layout.getEl();
51999             el.setSize(size.width, size.height);
52000             var touch = el.dom.offsetWidth;
52001             this.layout.layout();
52002             // ie requires a double layout on the first pass
52003             if(Roo.isIE && !this.initialized){
52004                 this.initialized = true;
52005                 this.layout.layout();
52006             }
52007         }
52008     },
52009     
52010     // activate all subpanels if not currently active..
52011     
52012     setActiveState : function(active){
52013         this.active = active;
52014         if(!active){
52015             this.fireEvent("deactivate", this);
52016             return;
52017         }
52018         
52019         this.fireEvent("activate", this);
52020         // not sure if this should happen before or after..
52021         if (!this.layout) {
52022             return; // should not happen..
52023         }
52024         var reg = false;
52025         for (var r in this.layout.regions) {
52026             reg = this.layout.getRegion(r);
52027             if (reg.getActivePanel()) {
52028                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
52029                 reg.setActivePanel(reg.getActivePanel());
52030                 continue;
52031             }
52032             if (!reg.panels.length) {
52033                 continue;
52034             }
52035             reg.showPanel(reg.getPanel(0));
52036         }
52037         
52038         
52039         
52040         
52041     },
52042     
52043     /**
52044      * Returns the nested BorderLayout for this panel
52045      * @return {Roo.BorderLayout} 
52046      */
52047     getLayout : function(){
52048         return this.layout;
52049     },
52050     
52051      /**
52052      * Adds a xtype elements to the layout of the nested panel
52053      * <pre><code>
52054
52055 panel.addxtype({
52056        xtype : 'ContentPanel',
52057        region: 'west',
52058        items: [ .... ]
52059    }
52060 );
52061
52062 panel.addxtype({
52063         xtype : 'NestedLayoutPanel',
52064         region: 'west',
52065         layout: {
52066            center: { },
52067            west: { }   
52068         },
52069         items : [ ... list of content panels or nested layout panels.. ]
52070    }
52071 );
52072 </code></pre>
52073      * @param {Object} cfg Xtype definition of item to add.
52074      */
52075     addxtype : function(cfg) {
52076         return this.layout.addxtype(cfg);
52077     
52078     }
52079 });
52080
52081 Roo.ScrollPanel = function(el, config, content){
52082     config = config || {};
52083     config.fitToFrame = true;
52084     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52085     
52086     this.el.dom.style.overflow = "hidden";
52087     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52088     this.el.removeClass("x-layout-inactive-content");
52089     this.el.on("mousewheel", this.onWheel, this);
52090
52091     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52092     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52093     up.unselectable(); down.unselectable();
52094     up.on("click", this.scrollUp, this);
52095     down.on("click", this.scrollDown, this);
52096     up.addClassOnOver("x-scroller-btn-over");
52097     down.addClassOnOver("x-scroller-btn-over");
52098     up.addClassOnClick("x-scroller-btn-click");
52099     down.addClassOnClick("x-scroller-btn-click");
52100     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52101
52102     this.resizeEl = this.el;
52103     this.el = wrap; this.up = up; this.down = down;
52104 };
52105
52106 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52107     increment : 100,
52108     wheelIncrement : 5,
52109     scrollUp : function(){
52110         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52111     },
52112
52113     scrollDown : function(){
52114         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52115     },
52116
52117     afterScroll : function(){
52118         var el = this.resizeEl;
52119         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52120         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52121         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52122     },
52123
52124     setSize : function(){
52125         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52126         this.afterScroll();
52127     },
52128
52129     onWheel : function(e){
52130         var d = e.getWheelDelta();
52131         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52132         this.afterScroll();
52133         e.stopEvent();
52134     },
52135
52136     setContent : function(content, loadScripts){
52137         this.resizeEl.update(content, loadScripts);
52138     }
52139
52140 });
52141
52142
52143
52144
52145
52146
52147
52148
52149
52150 /**
52151  * @class Roo.TreePanel
52152  * @extends Roo.ContentPanel
52153  * @constructor
52154  * Create a new TreePanel. - defaults to fit/scoll contents.
52155  * @param {String/Object} config A string to set only the panel's title, or a config object
52156  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52157  */
52158 Roo.TreePanel = function(config){
52159     var el = config.el;
52160     var tree = config.tree;
52161     delete config.tree; 
52162     delete config.el; // hopefull!
52163     
52164     // wrapper for IE7 strict & safari scroll issue
52165     
52166     var treeEl = el.createChild();
52167     config.resizeEl = treeEl;
52168     
52169     
52170     
52171     Roo.TreePanel.superclass.constructor.call(this, el, config);
52172  
52173  
52174     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52175     //console.log(tree);
52176     this.on('activate', function()
52177     {
52178         if (this.tree.rendered) {
52179             return;
52180         }
52181         //console.log('render tree');
52182         this.tree.render();
52183     });
52184     // this should not be needed.. - it's actually the 'el' that resizes?
52185     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52186     
52187     //this.on('resize',  function (cp, w, h) {
52188     //        this.tree.innerCt.setWidth(w);
52189     //        this.tree.innerCt.setHeight(h);
52190     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52191     //});
52192
52193         
52194     
52195 };
52196
52197 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52198     fitToFrame : true,
52199     autoScroll : true
52200 });
52201
52202
52203
52204
52205
52206
52207
52208
52209
52210
52211
52212 /*
52213  * Based on:
52214  * Ext JS Library 1.1.1
52215  * Copyright(c) 2006-2007, Ext JS, LLC.
52216  *
52217  * Originally Released Under LGPL - original licence link has changed is not relivant.
52218  *
52219  * Fork - LGPL
52220  * <script type="text/javascript">
52221  */
52222  
52223
52224 /**
52225  * @class Roo.ReaderLayout
52226  * @extends Roo.BorderLayout
52227  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52228  * center region containing two nested regions (a top one for a list view and one for item preview below),
52229  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52230  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52231  * expedites the setup of the overall layout and regions for this common application style.
52232  * Example:
52233  <pre><code>
52234 var reader = new Roo.ReaderLayout();
52235 var CP = Roo.ContentPanel;  // shortcut for adding
52236
52237 reader.beginUpdate();
52238 reader.add("north", new CP("north", "North"));
52239 reader.add("west", new CP("west", {title: "West"}));
52240 reader.add("east", new CP("east", {title: "East"}));
52241
52242 reader.regions.listView.add(new CP("listView", "List"));
52243 reader.regions.preview.add(new CP("preview", "Preview"));
52244 reader.endUpdate();
52245 </code></pre>
52246 * @constructor
52247 * Create a new ReaderLayout
52248 * @param {Object} config Configuration options
52249 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52250 * document.body if omitted)
52251 */
52252 Roo.ReaderLayout = function(config, renderTo){
52253     var c = config || {size:{}};
52254     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52255         north: c.north !== false ? Roo.apply({
52256             split:false,
52257             initialSize: 32,
52258             titlebar: false
52259         }, c.north) : false,
52260         west: c.west !== false ? Roo.apply({
52261             split:true,
52262             initialSize: 200,
52263             minSize: 175,
52264             maxSize: 400,
52265             titlebar: true,
52266             collapsible: true,
52267             animate: true,
52268             margins:{left:5,right:0,bottom:5,top:5},
52269             cmargins:{left:5,right:5,bottom:5,top:5}
52270         }, c.west) : false,
52271         east: c.east !== false ? Roo.apply({
52272             split:true,
52273             initialSize: 200,
52274             minSize: 175,
52275             maxSize: 400,
52276             titlebar: true,
52277             collapsible: true,
52278             animate: true,
52279             margins:{left:0,right:5,bottom:5,top:5},
52280             cmargins:{left:5,right:5,bottom:5,top:5}
52281         }, c.east) : false,
52282         center: Roo.apply({
52283             tabPosition: 'top',
52284             autoScroll:false,
52285             closeOnTab: true,
52286             titlebar:false,
52287             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52288         }, c.center)
52289     });
52290
52291     this.el.addClass('x-reader');
52292
52293     this.beginUpdate();
52294
52295     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52296         south: c.preview !== false ? Roo.apply({
52297             split:true,
52298             initialSize: 200,
52299             minSize: 100,
52300             autoScroll:true,
52301             collapsible:true,
52302             titlebar: true,
52303             cmargins:{top:5,left:0, right:0, bottom:0}
52304         }, c.preview) : false,
52305         center: Roo.apply({
52306             autoScroll:false,
52307             titlebar:false,
52308             minHeight:200
52309         }, c.listView)
52310     });
52311     this.add('center', new Roo.NestedLayoutPanel(inner,
52312             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52313
52314     this.endUpdate();
52315
52316     this.regions.preview = inner.getRegion('south');
52317     this.regions.listView = inner.getRegion('center');
52318 };
52319
52320 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52321  * Based on:
52322  * Ext JS Library 1.1.1
52323  * Copyright(c) 2006-2007, Ext JS, LLC.
52324  *
52325  * Originally Released Under LGPL - original licence link has changed is not relivant.
52326  *
52327  * Fork - LGPL
52328  * <script type="text/javascript">
52329  */
52330  
52331 /**
52332  * @class Roo.grid.Grid
52333  * @extends Roo.util.Observable
52334  * This class represents the primary interface of a component based grid control.
52335  * <br><br>Usage:<pre><code>
52336  var grid = new Roo.grid.Grid("my-container-id", {
52337      ds: myDataStore,
52338      cm: myColModel,
52339      selModel: mySelectionModel,
52340      autoSizeColumns: true,
52341      monitorWindowResize: false,
52342      trackMouseOver: true
52343  });
52344  // set any options
52345  grid.render();
52346  * </code></pre>
52347  * <b>Common Problems:</b><br/>
52348  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52349  * element will correct this<br/>
52350  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52351  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52352  * are unpredictable.<br/>
52353  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52354  * grid to calculate dimensions/offsets.<br/>
52355   * @constructor
52356  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52357  * The container MUST have some type of size defined for the grid to fill. The container will be
52358  * automatically set to position relative if it isn't already.
52359  * @param {Object} config A config object that sets properties on this grid.
52360  */
52361 Roo.grid.Grid = function(container, config){
52362         // initialize the container
52363         this.container = Roo.get(container);
52364         this.container.update("");
52365         this.container.setStyle("overflow", "hidden");
52366     this.container.addClass('x-grid-container');
52367
52368     this.id = this.container.id;
52369
52370     Roo.apply(this, config);
52371     // check and correct shorthanded configs
52372     if(this.ds){
52373         this.dataSource = this.ds;
52374         delete this.ds;
52375     }
52376     if(this.cm){
52377         this.colModel = this.cm;
52378         delete this.cm;
52379     }
52380     if(this.sm){
52381         this.selModel = this.sm;
52382         delete this.sm;
52383     }
52384
52385     if (this.selModel) {
52386         this.selModel = Roo.factory(this.selModel, Roo.grid);
52387         this.sm = this.selModel;
52388         this.sm.xmodule = this.xmodule || false;
52389     }
52390     if (typeof(this.colModel.config) == 'undefined') {
52391         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52392         this.cm = this.colModel;
52393         this.cm.xmodule = this.xmodule || false;
52394     }
52395     if (this.dataSource) {
52396         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52397         this.ds = this.dataSource;
52398         this.ds.xmodule = this.xmodule || false;
52399          
52400     }
52401     
52402     
52403     
52404     if(this.width){
52405         this.container.setWidth(this.width);
52406     }
52407
52408     if(this.height){
52409         this.container.setHeight(this.height);
52410     }
52411     /** @private */
52412         this.addEvents({
52413         // raw events
52414         /**
52415          * @event click
52416          * The raw click event for the entire grid.
52417          * @param {Roo.EventObject} e
52418          */
52419         "click" : true,
52420         /**
52421          * @event dblclick
52422          * The raw dblclick event for the entire grid.
52423          * @param {Roo.EventObject} e
52424          */
52425         "dblclick" : true,
52426         /**
52427          * @event contextmenu
52428          * The raw contextmenu event for the entire grid.
52429          * @param {Roo.EventObject} e
52430          */
52431         "contextmenu" : true,
52432         /**
52433          * @event mousedown
52434          * The raw mousedown event for the entire grid.
52435          * @param {Roo.EventObject} e
52436          */
52437         "mousedown" : true,
52438         /**
52439          * @event mouseup
52440          * The raw mouseup event for the entire grid.
52441          * @param {Roo.EventObject} e
52442          */
52443         "mouseup" : true,
52444         /**
52445          * @event mouseover
52446          * The raw mouseover event for the entire grid.
52447          * @param {Roo.EventObject} e
52448          */
52449         "mouseover" : true,
52450         /**
52451          * @event mouseout
52452          * The raw mouseout event for the entire grid.
52453          * @param {Roo.EventObject} e
52454          */
52455         "mouseout" : true,
52456         /**
52457          * @event keypress
52458          * The raw keypress event for the entire grid.
52459          * @param {Roo.EventObject} e
52460          */
52461         "keypress" : true,
52462         /**
52463          * @event keydown
52464          * The raw keydown event for the entire grid.
52465          * @param {Roo.EventObject} e
52466          */
52467         "keydown" : true,
52468
52469         // custom events
52470
52471         /**
52472          * @event cellclick
52473          * Fires when a cell is clicked
52474          * @param {Grid} this
52475          * @param {Number} rowIndex
52476          * @param {Number} columnIndex
52477          * @param {Roo.EventObject} e
52478          */
52479         "cellclick" : true,
52480         /**
52481          * @event celldblclick
52482          * Fires when a cell is double clicked
52483          * @param {Grid} this
52484          * @param {Number} rowIndex
52485          * @param {Number} columnIndex
52486          * @param {Roo.EventObject} e
52487          */
52488         "celldblclick" : true,
52489         /**
52490          * @event rowclick
52491          * Fires when a row is clicked
52492          * @param {Grid} this
52493          * @param {Number} rowIndex
52494          * @param {Roo.EventObject} e
52495          */
52496         "rowclick" : true,
52497         /**
52498          * @event rowdblclick
52499          * Fires when a row is double clicked
52500          * @param {Grid} this
52501          * @param {Number} rowIndex
52502          * @param {Roo.EventObject} e
52503          */
52504         "rowdblclick" : true,
52505         /**
52506          * @event headerclick
52507          * Fires when a header is clicked
52508          * @param {Grid} this
52509          * @param {Number} columnIndex
52510          * @param {Roo.EventObject} e
52511          */
52512         "headerclick" : true,
52513         /**
52514          * @event headerdblclick
52515          * Fires when a header cell is double clicked
52516          * @param {Grid} this
52517          * @param {Number} columnIndex
52518          * @param {Roo.EventObject} e
52519          */
52520         "headerdblclick" : true,
52521         /**
52522          * @event rowcontextmenu
52523          * Fires when a row is right clicked
52524          * @param {Grid} this
52525          * @param {Number} rowIndex
52526          * @param {Roo.EventObject} e
52527          */
52528         "rowcontextmenu" : true,
52529         /**
52530          * @event cellcontextmenu
52531          * Fires when a cell is right clicked
52532          * @param {Grid} this
52533          * @param {Number} rowIndex
52534          * @param {Number} cellIndex
52535          * @param {Roo.EventObject} e
52536          */
52537          "cellcontextmenu" : true,
52538         /**
52539          * @event headercontextmenu
52540          * Fires when a header is right clicked
52541          * @param {Grid} this
52542          * @param {Number} columnIndex
52543          * @param {Roo.EventObject} e
52544          */
52545         "headercontextmenu" : true,
52546         /**
52547          * @event bodyscroll
52548          * Fires when the body element is scrolled
52549          * @param {Number} scrollLeft
52550          * @param {Number} scrollTop
52551          */
52552         "bodyscroll" : true,
52553         /**
52554          * @event columnresize
52555          * Fires when the user resizes a column
52556          * @param {Number} columnIndex
52557          * @param {Number} newSize
52558          */
52559         "columnresize" : true,
52560         /**
52561          * @event columnmove
52562          * Fires when the user moves a column
52563          * @param {Number} oldIndex
52564          * @param {Number} newIndex
52565          */
52566         "columnmove" : true,
52567         /**
52568          * @event startdrag
52569          * Fires when row(s) start being dragged
52570          * @param {Grid} this
52571          * @param {Roo.GridDD} dd The drag drop object
52572          * @param {event} e The raw browser event
52573          */
52574         "startdrag" : true,
52575         /**
52576          * @event enddrag
52577          * Fires when a drag operation is complete
52578          * @param {Grid} this
52579          * @param {Roo.GridDD} dd The drag drop object
52580          * @param {event} e The raw browser event
52581          */
52582         "enddrag" : true,
52583         /**
52584          * @event dragdrop
52585          * Fires when dragged row(s) are dropped on a valid DD target
52586          * @param {Grid} this
52587          * @param {Roo.GridDD} dd The drag drop object
52588          * @param {String} targetId The target drag drop object
52589          * @param {event} e The raw browser event
52590          */
52591         "dragdrop" : true,
52592         /**
52593          * @event dragover
52594          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52595          * @param {Grid} this
52596          * @param {Roo.GridDD} dd The drag drop object
52597          * @param {String} targetId The target drag drop object
52598          * @param {event} e The raw browser event
52599          */
52600         "dragover" : true,
52601         /**
52602          * @event dragenter
52603          *  Fires when the dragged row(s) first cross another DD target while being dragged
52604          * @param {Grid} this
52605          * @param {Roo.GridDD} dd The drag drop object
52606          * @param {String} targetId The target drag drop object
52607          * @param {event} e The raw browser event
52608          */
52609         "dragenter" : true,
52610         /**
52611          * @event dragout
52612          * Fires when the dragged row(s) leave another DD target while being dragged
52613          * @param {Grid} this
52614          * @param {Roo.GridDD} dd The drag drop object
52615          * @param {String} targetId The target drag drop object
52616          * @param {event} e The raw browser event
52617          */
52618         "dragout" : true,
52619         /**
52620          * @event rowclass
52621          * Fires when a row is rendered, so you can change add a style to it.
52622          * @param {GridView} gridview   The grid view
52623          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52624          */
52625         'rowclass' : true,
52626
52627         /**
52628          * @event render
52629          * Fires when the grid is rendered
52630          * @param {Grid} grid
52631          */
52632         'render' : true
52633     });
52634
52635     Roo.grid.Grid.superclass.constructor.call(this);
52636 };
52637 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52638     
52639     /**
52640      * @cfg {String} ddGroup - drag drop group.
52641      */
52642
52643     /**
52644      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52645      */
52646     minColumnWidth : 25,
52647
52648     /**
52649      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52650      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52651      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52652      */
52653     autoSizeColumns : false,
52654
52655     /**
52656      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52657      */
52658     autoSizeHeaders : true,
52659
52660     /**
52661      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52662      */
52663     monitorWindowResize : true,
52664
52665     /**
52666      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52667      * rows measured to get a columns size. Default is 0 (all rows).
52668      */
52669     maxRowsToMeasure : 0,
52670
52671     /**
52672      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52673      */
52674     trackMouseOver : true,
52675
52676     /**
52677     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52678     */
52679     
52680     /**
52681     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52682     */
52683     enableDragDrop : false,
52684     
52685     /**
52686     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52687     */
52688     enableColumnMove : true,
52689     
52690     /**
52691     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52692     */
52693     enableColumnHide : true,
52694     
52695     /**
52696     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52697     */
52698     enableRowHeightSync : false,
52699     
52700     /**
52701     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52702     */
52703     stripeRows : true,
52704     
52705     /**
52706     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52707     */
52708     autoHeight : false,
52709
52710     /**
52711      * @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.
52712      */
52713     autoExpandColumn : false,
52714
52715     /**
52716     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52717     * Default is 50.
52718     */
52719     autoExpandMin : 50,
52720
52721     /**
52722     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52723     */
52724     autoExpandMax : 1000,
52725
52726     /**
52727     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52728     */
52729     view : null,
52730
52731     /**
52732     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52733     */
52734     loadMask : false,
52735     /**
52736     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52737     */
52738     dropTarget: false,
52739     
52740    
52741     
52742     // private
52743     rendered : false,
52744
52745     /**
52746     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52747     * of a fixed width. Default is false.
52748     */
52749     /**
52750     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52751     */
52752     /**
52753      * Called once after all setup has been completed and the grid is ready to be rendered.
52754      * @return {Roo.grid.Grid} this
52755      */
52756     render : function()
52757     {
52758         var c = this.container;
52759         // try to detect autoHeight/width mode
52760         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52761             this.autoHeight = true;
52762         }
52763         var view = this.getView();
52764         view.init(this);
52765
52766         c.on("click", this.onClick, this);
52767         c.on("dblclick", this.onDblClick, this);
52768         c.on("contextmenu", this.onContextMenu, this);
52769         c.on("keydown", this.onKeyDown, this);
52770         if (Roo.isTouch) {
52771             c.on("touchstart", this.onTouchStart, this);
52772         }
52773
52774         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52775
52776         this.getSelectionModel().init(this);
52777
52778         view.render();
52779
52780         if(this.loadMask){
52781             this.loadMask = new Roo.LoadMask(this.container,
52782                     Roo.apply({store:this.dataSource}, this.loadMask));
52783         }
52784         
52785         
52786         if (this.toolbar && this.toolbar.xtype) {
52787             this.toolbar.container = this.getView().getHeaderPanel(true);
52788             this.toolbar = new Roo.Toolbar(this.toolbar);
52789         }
52790         if (this.footer && this.footer.xtype) {
52791             this.footer.dataSource = this.getDataSource();
52792             this.footer.container = this.getView().getFooterPanel(true);
52793             this.footer = Roo.factory(this.footer, Roo);
52794         }
52795         if (this.dropTarget && this.dropTarget.xtype) {
52796             delete this.dropTarget.xtype;
52797             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52798         }
52799         
52800         
52801         this.rendered = true;
52802         this.fireEvent('render', this);
52803         return this;
52804     },
52805
52806         /**
52807          * Reconfigures the grid to use a different Store and Column Model.
52808          * The View will be bound to the new objects and refreshed.
52809          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52810          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52811          */
52812     reconfigure : function(dataSource, colModel){
52813         if(this.loadMask){
52814             this.loadMask.destroy();
52815             this.loadMask = new Roo.LoadMask(this.container,
52816                     Roo.apply({store:dataSource}, this.loadMask));
52817         }
52818         this.view.bind(dataSource, colModel);
52819         this.dataSource = dataSource;
52820         this.colModel = colModel;
52821         this.view.refresh(true);
52822     },
52823
52824     // private
52825     onKeyDown : function(e){
52826         this.fireEvent("keydown", e);
52827     },
52828
52829     /**
52830      * Destroy this grid.
52831      * @param {Boolean} removeEl True to remove the element
52832      */
52833     destroy : function(removeEl, keepListeners){
52834         if(this.loadMask){
52835             this.loadMask.destroy();
52836         }
52837         var c = this.container;
52838         c.removeAllListeners();
52839         this.view.destroy();
52840         this.colModel.purgeListeners();
52841         if(!keepListeners){
52842             this.purgeListeners();
52843         }
52844         c.update("");
52845         if(removeEl === true){
52846             c.remove();
52847         }
52848     },
52849
52850     // private
52851     processEvent : function(name, e){
52852         // does this fire select???
52853         //Roo.log('grid:processEvent '  + name);
52854         
52855         if (name != 'touchstart' ) {
52856             this.fireEvent(name, e);    
52857         }
52858         
52859         var t = e.getTarget();
52860         var v = this.view;
52861         var header = v.findHeaderIndex(t);
52862         if(header !== false){
52863             var ename = name == 'touchstart' ? 'click' : name;
52864              
52865             this.fireEvent("header" + ename, this, header, e);
52866         }else{
52867             var row = v.findRowIndex(t);
52868             var cell = v.findCellIndex(t);
52869             if (name == 'touchstart') {
52870                 // first touch is always a click.
52871                 // hopefull this happens after selection is updated.?
52872                 name = false;
52873                 
52874                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52875                     var cs = this.selModel.getSelectedCell();
52876                     if (row == cs[0] && cell == cs[1]){
52877                         name = 'dblclick';
52878                     }
52879                 }
52880                 if (typeof(this.selModel.getSelections) != 'undefined') {
52881                     var cs = this.selModel.getSelections();
52882                     var ds = this.dataSource;
52883                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52884                         name = 'dblclick';
52885                     }
52886                 }
52887                 if (!name) {
52888                     return;
52889                 }
52890             }
52891             
52892             
52893             if(row !== false){
52894                 this.fireEvent("row" + name, this, row, e);
52895                 if(cell !== false){
52896                     this.fireEvent("cell" + name, this, row, cell, e);
52897                 }
52898             }
52899         }
52900     },
52901
52902     // private
52903     onClick : function(e){
52904         this.processEvent("click", e);
52905     },
52906    // private
52907     onTouchStart : function(e){
52908         this.processEvent("touchstart", e);
52909     },
52910
52911     // private
52912     onContextMenu : function(e, t){
52913         this.processEvent("contextmenu", e);
52914     },
52915
52916     // private
52917     onDblClick : function(e){
52918         this.processEvent("dblclick", e);
52919     },
52920
52921     // private
52922     walkCells : function(row, col, step, fn, scope){
52923         var cm = this.colModel, clen = cm.getColumnCount();
52924         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52925         if(step < 0){
52926             if(col < 0){
52927                 row--;
52928                 first = false;
52929             }
52930             while(row >= 0){
52931                 if(!first){
52932                     col = clen-1;
52933                 }
52934                 first = false;
52935                 while(col >= 0){
52936                     if(fn.call(scope || this, row, col, cm) === true){
52937                         return [row, col];
52938                     }
52939                     col--;
52940                 }
52941                 row--;
52942             }
52943         } else {
52944             if(col >= clen){
52945                 row++;
52946                 first = false;
52947             }
52948             while(row < rlen){
52949                 if(!first){
52950                     col = 0;
52951                 }
52952                 first = false;
52953                 while(col < clen){
52954                     if(fn.call(scope || this, row, col, cm) === true){
52955                         return [row, col];
52956                     }
52957                     col++;
52958                 }
52959                 row++;
52960             }
52961         }
52962         return null;
52963     },
52964
52965     // private
52966     getSelections : function(){
52967         return this.selModel.getSelections();
52968     },
52969
52970     /**
52971      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52972      * but if manual update is required this method will initiate it.
52973      */
52974     autoSize : function(){
52975         if(this.rendered){
52976             this.view.layout();
52977             if(this.view.adjustForScroll){
52978                 this.view.adjustForScroll();
52979             }
52980         }
52981     },
52982
52983     /**
52984      * Returns the grid's underlying element.
52985      * @return {Element} The element
52986      */
52987     getGridEl : function(){
52988         return this.container;
52989     },
52990
52991     // private for compatibility, overridden by editor grid
52992     stopEditing : function(){},
52993
52994     /**
52995      * Returns the grid's SelectionModel.
52996      * @return {SelectionModel}
52997      */
52998     getSelectionModel : function(){
52999         if(!this.selModel){
53000             this.selModel = new Roo.grid.RowSelectionModel();
53001         }
53002         return this.selModel;
53003     },
53004
53005     /**
53006      * Returns the grid's DataSource.
53007      * @return {DataSource}
53008      */
53009     getDataSource : function(){
53010         return this.dataSource;
53011     },
53012
53013     /**
53014      * Returns the grid's ColumnModel.
53015      * @return {ColumnModel}
53016      */
53017     getColumnModel : function(){
53018         return this.colModel;
53019     },
53020
53021     /**
53022      * Returns the grid's GridView object.
53023      * @return {GridView}
53024      */
53025     getView : function(){
53026         if(!this.view){
53027             this.view = new Roo.grid.GridView(this.viewConfig);
53028         }
53029         return this.view;
53030     },
53031     /**
53032      * Called to get grid's drag proxy text, by default returns this.ddText.
53033      * @return {String}
53034      */
53035     getDragDropText : function(){
53036         var count = this.selModel.getCount();
53037         return String.format(this.ddText, count, count == 1 ? '' : 's');
53038     }
53039 });
53040 /**
53041  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53042  * %0 is replaced with the number of selected rows.
53043  * @type String
53044  */
53045 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53046  * Based on:
53047  * Ext JS Library 1.1.1
53048  * Copyright(c) 2006-2007, Ext JS, LLC.
53049  *
53050  * Originally Released Under LGPL - original licence link has changed is not relivant.
53051  *
53052  * Fork - LGPL
53053  * <script type="text/javascript">
53054  */
53055  
53056 Roo.grid.AbstractGridView = function(){
53057         this.grid = null;
53058         
53059         this.events = {
53060             "beforerowremoved" : true,
53061             "beforerowsinserted" : true,
53062             "beforerefresh" : true,
53063             "rowremoved" : true,
53064             "rowsinserted" : true,
53065             "rowupdated" : true,
53066             "refresh" : true
53067         };
53068     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53069 };
53070
53071 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53072     rowClass : "x-grid-row",
53073     cellClass : "x-grid-cell",
53074     tdClass : "x-grid-td",
53075     hdClass : "x-grid-hd",
53076     splitClass : "x-grid-hd-split",
53077     
53078     init: function(grid){
53079         this.grid = grid;
53080                 var cid = this.grid.getGridEl().id;
53081         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53082         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53083         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53084         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53085         },
53086         
53087     getColumnRenderers : function(){
53088         var renderers = [];
53089         var cm = this.grid.colModel;
53090         var colCount = cm.getColumnCount();
53091         for(var i = 0; i < colCount; i++){
53092             renderers[i] = cm.getRenderer(i);
53093         }
53094         return renderers;
53095     },
53096     
53097     getColumnIds : function(){
53098         var ids = [];
53099         var cm = this.grid.colModel;
53100         var colCount = cm.getColumnCount();
53101         for(var i = 0; i < colCount; i++){
53102             ids[i] = cm.getColumnId(i);
53103         }
53104         return ids;
53105     },
53106     
53107     getDataIndexes : function(){
53108         if(!this.indexMap){
53109             this.indexMap = this.buildIndexMap();
53110         }
53111         return this.indexMap.colToData;
53112     },
53113     
53114     getColumnIndexByDataIndex : function(dataIndex){
53115         if(!this.indexMap){
53116             this.indexMap = this.buildIndexMap();
53117         }
53118         return this.indexMap.dataToCol[dataIndex];
53119     },
53120     
53121     /**
53122      * Set a css style for a column dynamically. 
53123      * @param {Number} colIndex The index of the column
53124      * @param {String} name The css property name
53125      * @param {String} value The css value
53126      */
53127     setCSSStyle : function(colIndex, name, value){
53128         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53129         Roo.util.CSS.updateRule(selector, name, value);
53130     },
53131     
53132     generateRules : function(cm){
53133         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53134         Roo.util.CSS.removeStyleSheet(rulesId);
53135         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53136             var cid = cm.getColumnId(i);
53137             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53138                          this.tdSelector, cid, " {\n}\n",
53139                          this.hdSelector, cid, " {\n}\n",
53140                          this.splitSelector, cid, " {\n}\n");
53141         }
53142         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53143     }
53144 });/*
53145  * Based on:
53146  * Ext JS Library 1.1.1
53147  * Copyright(c) 2006-2007, Ext JS, LLC.
53148  *
53149  * Originally Released Under LGPL - original licence link has changed is not relivant.
53150  *
53151  * Fork - LGPL
53152  * <script type="text/javascript">
53153  */
53154
53155 // private
53156 // This is a support class used internally by the Grid components
53157 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53158     this.grid = grid;
53159     this.view = grid.getView();
53160     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53161     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53162     if(hd2){
53163         this.setHandleElId(Roo.id(hd));
53164         this.setOuterHandleElId(Roo.id(hd2));
53165     }
53166     this.scroll = false;
53167 };
53168 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53169     maxDragWidth: 120,
53170     getDragData : function(e){
53171         var t = Roo.lib.Event.getTarget(e);
53172         var h = this.view.findHeaderCell(t);
53173         if(h){
53174             return {ddel: h.firstChild, header:h};
53175         }
53176         return false;
53177     },
53178
53179     onInitDrag : function(e){
53180         this.view.headersDisabled = true;
53181         var clone = this.dragData.ddel.cloneNode(true);
53182         clone.id = Roo.id();
53183         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53184         this.proxy.update(clone);
53185         return true;
53186     },
53187
53188     afterValidDrop : function(){
53189         var v = this.view;
53190         setTimeout(function(){
53191             v.headersDisabled = false;
53192         }, 50);
53193     },
53194
53195     afterInvalidDrop : function(){
53196         var v = this.view;
53197         setTimeout(function(){
53198             v.headersDisabled = false;
53199         }, 50);
53200     }
53201 });
53202 /*
53203  * Based on:
53204  * Ext JS Library 1.1.1
53205  * Copyright(c) 2006-2007, Ext JS, LLC.
53206  *
53207  * Originally Released Under LGPL - original licence link has changed is not relivant.
53208  *
53209  * Fork - LGPL
53210  * <script type="text/javascript">
53211  */
53212 // private
53213 // This is a support class used internally by the Grid components
53214 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53215     this.grid = grid;
53216     this.view = grid.getView();
53217     // split the proxies so they don't interfere with mouse events
53218     this.proxyTop = Roo.DomHelper.append(document.body, {
53219         cls:"col-move-top", html:"&#160;"
53220     }, true);
53221     this.proxyBottom = Roo.DomHelper.append(document.body, {
53222         cls:"col-move-bottom", html:"&#160;"
53223     }, true);
53224     this.proxyTop.hide = this.proxyBottom.hide = function(){
53225         this.setLeftTop(-100,-100);
53226         this.setStyle("visibility", "hidden");
53227     };
53228     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53229     // temporarily disabled
53230     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53231     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53232 };
53233 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53234     proxyOffsets : [-4, -9],
53235     fly: Roo.Element.fly,
53236
53237     getTargetFromEvent : function(e){
53238         var t = Roo.lib.Event.getTarget(e);
53239         var cindex = this.view.findCellIndex(t);
53240         if(cindex !== false){
53241             return this.view.getHeaderCell(cindex);
53242         }
53243         return null;
53244     },
53245
53246     nextVisible : function(h){
53247         var v = this.view, cm = this.grid.colModel;
53248         h = h.nextSibling;
53249         while(h){
53250             if(!cm.isHidden(v.getCellIndex(h))){
53251                 return h;
53252             }
53253             h = h.nextSibling;
53254         }
53255         return null;
53256     },
53257
53258     prevVisible : function(h){
53259         var v = this.view, cm = this.grid.colModel;
53260         h = h.prevSibling;
53261         while(h){
53262             if(!cm.isHidden(v.getCellIndex(h))){
53263                 return h;
53264             }
53265             h = h.prevSibling;
53266         }
53267         return null;
53268     },
53269
53270     positionIndicator : function(h, n, e){
53271         var x = Roo.lib.Event.getPageX(e);
53272         var r = Roo.lib.Dom.getRegion(n.firstChild);
53273         var px, pt, py = r.top + this.proxyOffsets[1];
53274         if((r.right - x) <= (r.right-r.left)/2){
53275             px = r.right+this.view.borderWidth;
53276             pt = "after";
53277         }else{
53278             px = r.left;
53279             pt = "before";
53280         }
53281         var oldIndex = this.view.getCellIndex(h);
53282         var newIndex = this.view.getCellIndex(n);
53283
53284         if(this.grid.colModel.isFixed(newIndex)){
53285             return false;
53286         }
53287
53288         var locked = this.grid.colModel.isLocked(newIndex);
53289
53290         if(pt == "after"){
53291             newIndex++;
53292         }
53293         if(oldIndex < newIndex){
53294             newIndex--;
53295         }
53296         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53297             return false;
53298         }
53299         px +=  this.proxyOffsets[0];
53300         this.proxyTop.setLeftTop(px, py);
53301         this.proxyTop.show();
53302         if(!this.bottomOffset){
53303             this.bottomOffset = this.view.mainHd.getHeight();
53304         }
53305         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53306         this.proxyBottom.show();
53307         return pt;
53308     },
53309
53310     onNodeEnter : function(n, dd, e, data){
53311         if(data.header != n){
53312             this.positionIndicator(data.header, n, e);
53313         }
53314     },
53315
53316     onNodeOver : function(n, dd, e, data){
53317         var result = false;
53318         if(data.header != n){
53319             result = this.positionIndicator(data.header, n, e);
53320         }
53321         if(!result){
53322             this.proxyTop.hide();
53323             this.proxyBottom.hide();
53324         }
53325         return result ? this.dropAllowed : this.dropNotAllowed;
53326     },
53327
53328     onNodeOut : function(n, dd, e, data){
53329         this.proxyTop.hide();
53330         this.proxyBottom.hide();
53331     },
53332
53333     onNodeDrop : function(n, dd, e, data){
53334         var h = data.header;
53335         if(h != n){
53336             var cm = this.grid.colModel;
53337             var x = Roo.lib.Event.getPageX(e);
53338             var r = Roo.lib.Dom.getRegion(n.firstChild);
53339             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53340             var oldIndex = this.view.getCellIndex(h);
53341             var newIndex = this.view.getCellIndex(n);
53342             var locked = cm.isLocked(newIndex);
53343             if(pt == "after"){
53344                 newIndex++;
53345             }
53346             if(oldIndex < newIndex){
53347                 newIndex--;
53348             }
53349             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53350                 return false;
53351             }
53352             cm.setLocked(oldIndex, locked, true);
53353             cm.moveColumn(oldIndex, newIndex);
53354             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53355             return true;
53356         }
53357         return false;
53358     }
53359 });
53360 /*
53361  * Based on:
53362  * Ext JS Library 1.1.1
53363  * Copyright(c) 2006-2007, Ext JS, LLC.
53364  *
53365  * Originally Released Under LGPL - original licence link has changed is not relivant.
53366  *
53367  * Fork - LGPL
53368  * <script type="text/javascript">
53369  */
53370   
53371 /**
53372  * @class Roo.grid.GridView
53373  * @extends Roo.util.Observable
53374  *
53375  * @constructor
53376  * @param {Object} config
53377  */
53378 Roo.grid.GridView = function(config){
53379     Roo.grid.GridView.superclass.constructor.call(this);
53380     this.el = null;
53381
53382     Roo.apply(this, config);
53383 };
53384
53385 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53386
53387     unselectable :  'unselectable="on"',
53388     unselectableCls :  'x-unselectable',
53389     
53390     
53391     rowClass : "x-grid-row",
53392
53393     cellClass : "x-grid-col",
53394
53395     tdClass : "x-grid-td",
53396
53397     hdClass : "x-grid-hd",
53398
53399     splitClass : "x-grid-split",
53400
53401     sortClasses : ["sort-asc", "sort-desc"],
53402
53403     enableMoveAnim : false,
53404
53405     hlColor: "C3DAF9",
53406
53407     dh : Roo.DomHelper,
53408
53409     fly : Roo.Element.fly,
53410
53411     css : Roo.util.CSS,
53412
53413     borderWidth: 1,
53414
53415     splitOffset: 3,
53416
53417     scrollIncrement : 22,
53418
53419     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53420
53421     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53422
53423     bind : function(ds, cm){
53424         if(this.ds){
53425             this.ds.un("load", this.onLoad, this);
53426             this.ds.un("datachanged", this.onDataChange, this);
53427             this.ds.un("add", this.onAdd, this);
53428             this.ds.un("remove", this.onRemove, this);
53429             this.ds.un("update", this.onUpdate, this);
53430             this.ds.un("clear", this.onClear, this);
53431         }
53432         if(ds){
53433             ds.on("load", this.onLoad, this);
53434             ds.on("datachanged", this.onDataChange, this);
53435             ds.on("add", this.onAdd, this);
53436             ds.on("remove", this.onRemove, this);
53437             ds.on("update", this.onUpdate, this);
53438             ds.on("clear", this.onClear, this);
53439         }
53440         this.ds = ds;
53441
53442         if(this.cm){
53443             this.cm.un("widthchange", this.onColWidthChange, this);
53444             this.cm.un("headerchange", this.onHeaderChange, this);
53445             this.cm.un("hiddenchange", this.onHiddenChange, this);
53446             this.cm.un("columnmoved", this.onColumnMove, this);
53447             this.cm.un("columnlockchange", this.onColumnLock, this);
53448         }
53449         if(cm){
53450             this.generateRules(cm);
53451             cm.on("widthchange", this.onColWidthChange, this);
53452             cm.on("headerchange", this.onHeaderChange, this);
53453             cm.on("hiddenchange", this.onHiddenChange, this);
53454             cm.on("columnmoved", this.onColumnMove, this);
53455             cm.on("columnlockchange", this.onColumnLock, this);
53456         }
53457         this.cm = cm;
53458     },
53459
53460     init: function(grid){
53461         Roo.grid.GridView.superclass.init.call(this, grid);
53462
53463         this.bind(grid.dataSource, grid.colModel);
53464
53465         grid.on("headerclick", this.handleHeaderClick, this);
53466
53467         if(grid.trackMouseOver){
53468             grid.on("mouseover", this.onRowOver, this);
53469             grid.on("mouseout", this.onRowOut, this);
53470         }
53471         grid.cancelTextSelection = function(){};
53472         this.gridId = grid.id;
53473
53474         var tpls = this.templates || {};
53475
53476         if(!tpls.master){
53477             tpls.master = new Roo.Template(
53478                '<div class="x-grid" hidefocus="true">',
53479                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53480                   '<div class="x-grid-topbar"></div>',
53481                   '<div class="x-grid-scroller"><div></div></div>',
53482                   '<div class="x-grid-locked">',
53483                       '<div class="x-grid-header">{lockedHeader}</div>',
53484                       '<div class="x-grid-body">{lockedBody}</div>',
53485                   "</div>",
53486                   '<div class="x-grid-viewport">',
53487                       '<div class="x-grid-header">{header}</div>',
53488                       '<div class="x-grid-body">{body}</div>',
53489                   "</div>",
53490                   '<div class="x-grid-bottombar"></div>',
53491                  
53492                   '<div class="x-grid-resize-proxy">&#160;</div>',
53493                "</div>"
53494             );
53495             tpls.master.disableformats = true;
53496         }
53497
53498         if(!tpls.header){
53499             tpls.header = new Roo.Template(
53500                '<table border="0" cellspacing="0" cellpadding="0">',
53501                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53502                "</table>{splits}"
53503             );
53504             tpls.header.disableformats = true;
53505         }
53506         tpls.header.compile();
53507
53508         if(!tpls.hcell){
53509             tpls.hcell = new Roo.Template(
53510                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53511                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53512                 "</div></td>"
53513              );
53514              tpls.hcell.disableFormats = true;
53515         }
53516         tpls.hcell.compile();
53517
53518         if(!tpls.hsplit){
53519             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53520                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53521             tpls.hsplit.disableFormats = true;
53522         }
53523         tpls.hsplit.compile();
53524
53525         if(!tpls.body){
53526             tpls.body = new Roo.Template(
53527                '<table border="0" cellspacing="0" cellpadding="0">',
53528                "<tbody>{rows}</tbody>",
53529                "</table>"
53530             );
53531             tpls.body.disableFormats = true;
53532         }
53533         tpls.body.compile();
53534
53535         if(!tpls.row){
53536             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53537             tpls.row.disableFormats = true;
53538         }
53539         tpls.row.compile();
53540
53541         if(!tpls.cell){
53542             tpls.cell = new Roo.Template(
53543                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53544                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53545                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53546                 "</td>"
53547             );
53548             tpls.cell.disableFormats = true;
53549         }
53550         tpls.cell.compile();
53551
53552         this.templates = tpls;
53553     },
53554
53555     // remap these for backwards compat
53556     onColWidthChange : function(){
53557         this.updateColumns.apply(this, arguments);
53558     },
53559     onHeaderChange : function(){
53560         this.updateHeaders.apply(this, arguments);
53561     }, 
53562     onHiddenChange : function(){
53563         this.handleHiddenChange.apply(this, arguments);
53564     },
53565     onColumnMove : function(){
53566         this.handleColumnMove.apply(this, arguments);
53567     },
53568     onColumnLock : function(){
53569         this.handleLockChange.apply(this, arguments);
53570     },
53571
53572     onDataChange : function(){
53573         this.refresh();
53574         this.updateHeaderSortState();
53575     },
53576
53577     onClear : function(){
53578         this.refresh();
53579     },
53580
53581     onUpdate : function(ds, record){
53582         this.refreshRow(record);
53583     },
53584
53585     refreshRow : function(record){
53586         var ds = this.ds, index;
53587         if(typeof record == 'number'){
53588             index = record;
53589             record = ds.getAt(index);
53590         }else{
53591             index = ds.indexOf(record);
53592         }
53593         this.insertRows(ds, index, index, true);
53594         this.onRemove(ds, record, index+1, true);
53595         this.syncRowHeights(index, index);
53596         this.layout();
53597         this.fireEvent("rowupdated", this, index, record);
53598     },
53599
53600     onAdd : function(ds, records, index){
53601         this.insertRows(ds, index, index + (records.length-1));
53602     },
53603
53604     onRemove : function(ds, record, index, isUpdate){
53605         if(isUpdate !== true){
53606             this.fireEvent("beforerowremoved", this, index, record);
53607         }
53608         var bt = this.getBodyTable(), lt = this.getLockedTable();
53609         if(bt.rows[index]){
53610             bt.firstChild.removeChild(bt.rows[index]);
53611         }
53612         if(lt.rows[index]){
53613             lt.firstChild.removeChild(lt.rows[index]);
53614         }
53615         if(isUpdate !== true){
53616             this.stripeRows(index);
53617             this.syncRowHeights(index, index);
53618             this.layout();
53619             this.fireEvent("rowremoved", this, index, record);
53620         }
53621     },
53622
53623     onLoad : function(){
53624         this.scrollToTop();
53625     },
53626
53627     /**
53628      * Scrolls the grid to the top
53629      */
53630     scrollToTop : function(){
53631         if(this.scroller){
53632             this.scroller.dom.scrollTop = 0;
53633             this.syncScroll();
53634         }
53635     },
53636
53637     /**
53638      * Gets a panel in the header of the grid that can be used for toolbars etc.
53639      * After modifying the contents of this panel a call to grid.autoSize() may be
53640      * required to register any changes in size.
53641      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53642      * @return Roo.Element
53643      */
53644     getHeaderPanel : function(doShow){
53645         if(doShow){
53646             this.headerPanel.show();
53647         }
53648         return this.headerPanel;
53649     },
53650
53651     /**
53652      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53653      * After modifying the contents of this panel a call to grid.autoSize() may be
53654      * required to register any changes in size.
53655      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53656      * @return Roo.Element
53657      */
53658     getFooterPanel : function(doShow){
53659         if(doShow){
53660             this.footerPanel.show();
53661         }
53662         return this.footerPanel;
53663     },
53664
53665     initElements : function(){
53666         var E = Roo.Element;
53667         var el = this.grid.getGridEl().dom.firstChild;
53668         var cs = el.childNodes;
53669
53670         this.el = new E(el);
53671         
53672          this.focusEl = new E(el.firstChild);
53673         this.focusEl.swallowEvent("click", true);
53674         
53675         this.headerPanel = new E(cs[1]);
53676         this.headerPanel.enableDisplayMode("block");
53677
53678         this.scroller = new E(cs[2]);
53679         this.scrollSizer = new E(this.scroller.dom.firstChild);
53680
53681         this.lockedWrap = new E(cs[3]);
53682         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53683         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53684
53685         this.mainWrap = new E(cs[4]);
53686         this.mainHd = new E(this.mainWrap.dom.firstChild);
53687         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53688
53689         this.footerPanel = new E(cs[5]);
53690         this.footerPanel.enableDisplayMode("block");
53691
53692         this.resizeProxy = new E(cs[6]);
53693
53694         this.headerSelector = String.format(
53695            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53696            this.lockedHd.id, this.mainHd.id
53697         );
53698
53699         this.splitterSelector = String.format(
53700            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53701            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53702         );
53703     },
53704     idToCssName : function(s)
53705     {
53706         return s.replace(/[^a-z0-9]+/ig, '-');
53707     },
53708
53709     getHeaderCell : function(index){
53710         return Roo.DomQuery.select(this.headerSelector)[index];
53711     },
53712
53713     getHeaderCellMeasure : function(index){
53714         return this.getHeaderCell(index).firstChild;
53715     },
53716
53717     getHeaderCellText : function(index){
53718         return this.getHeaderCell(index).firstChild.firstChild;
53719     },
53720
53721     getLockedTable : function(){
53722         return this.lockedBody.dom.firstChild;
53723     },
53724
53725     getBodyTable : function(){
53726         return this.mainBody.dom.firstChild;
53727     },
53728
53729     getLockedRow : function(index){
53730         return this.getLockedTable().rows[index];
53731     },
53732
53733     getRow : function(index){
53734         return this.getBodyTable().rows[index];
53735     },
53736
53737     getRowComposite : function(index){
53738         if(!this.rowEl){
53739             this.rowEl = new Roo.CompositeElementLite();
53740         }
53741         var els = [], lrow, mrow;
53742         if(lrow = this.getLockedRow(index)){
53743             els.push(lrow);
53744         }
53745         if(mrow = this.getRow(index)){
53746             els.push(mrow);
53747         }
53748         this.rowEl.elements = els;
53749         return this.rowEl;
53750     },
53751     /**
53752      * Gets the 'td' of the cell
53753      * 
53754      * @param {Integer} rowIndex row to select
53755      * @param {Integer} colIndex column to select
53756      * 
53757      * @return {Object} 
53758      */
53759     getCell : function(rowIndex, colIndex){
53760         var locked = this.cm.getLockedCount();
53761         var source;
53762         if(colIndex < locked){
53763             source = this.lockedBody.dom.firstChild;
53764         }else{
53765             source = this.mainBody.dom.firstChild;
53766             colIndex -= locked;
53767         }
53768         return source.rows[rowIndex].childNodes[colIndex];
53769     },
53770
53771     getCellText : function(rowIndex, colIndex){
53772         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53773     },
53774
53775     getCellBox : function(cell){
53776         var b = this.fly(cell).getBox();
53777         if(Roo.isOpera){ // opera fails to report the Y
53778             b.y = cell.offsetTop + this.mainBody.getY();
53779         }
53780         return b;
53781     },
53782
53783     getCellIndex : function(cell){
53784         var id = String(cell.className).match(this.cellRE);
53785         if(id){
53786             return parseInt(id[1], 10);
53787         }
53788         return 0;
53789     },
53790
53791     findHeaderIndex : function(n){
53792         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53793         return r ? this.getCellIndex(r) : false;
53794     },
53795
53796     findHeaderCell : function(n){
53797         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53798         return r ? r : false;
53799     },
53800
53801     findRowIndex : function(n){
53802         if(!n){
53803             return false;
53804         }
53805         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53806         return r ? r.rowIndex : false;
53807     },
53808
53809     findCellIndex : function(node){
53810         var stop = this.el.dom;
53811         while(node && node != stop){
53812             if(this.findRE.test(node.className)){
53813                 return this.getCellIndex(node);
53814             }
53815             node = node.parentNode;
53816         }
53817         return false;
53818     },
53819
53820     getColumnId : function(index){
53821         return this.cm.getColumnId(index);
53822     },
53823
53824     getSplitters : function()
53825     {
53826         if(this.splitterSelector){
53827            return Roo.DomQuery.select(this.splitterSelector);
53828         }else{
53829             return null;
53830       }
53831     },
53832
53833     getSplitter : function(index){
53834         return this.getSplitters()[index];
53835     },
53836
53837     onRowOver : function(e, t){
53838         var row;
53839         if((row = this.findRowIndex(t)) !== false){
53840             this.getRowComposite(row).addClass("x-grid-row-over");
53841         }
53842     },
53843
53844     onRowOut : function(e, t){
53845         var row;
53846         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53847             this.getRowComposite(row).removeClass("x-grid-row-over");
53848         }
53849     },
53850
53851     renderHeaders : function(){
53852         var cm = this.cm;
53853         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53854         var cb = [], lb = [], sb = [], lsb = [], p = {};
53855         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53856             p.cellId = "x-grid-hd-0-" + i;
53857             p.splitId = "x-grid-csplit-0-" + i;
53858             p.id = cm.getColumnId(i);
53859             p.title = cm.getColumnTooltip(i) || "";
53860             p.value = cm.getColumnHeader(i) || "";
53861             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53862             if(!cm.isLocked(i)){
53863                 cb[cb.length] = ct.apply(p);
53864                 sb[sb.length] = st.apply(p);
53865             }else{
53866                 lb[lb.length] = ct.apply(p);
53867                 lsb[lsb.length] = st.apply(p);
53868             }
53869         }
53870         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53871                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53872     },
53873
53874     updateHeaders : function(){
53875         var html = this.renderHeaders();
53876         this.lockedHd.update(html[0]);
53877         this.mainHd.update(html[1]);
53878     },
53879
53880     /**
53881      * Focuses the specified row.
53882      * @param {Number} row The row index
53883      */
53884     focusRow : function(row)
53885     {
53886         //Roo.log('GridView.focusRow');
53887         var x = this.scroller.dom.scrollLeft;
53888         this.focusCell(row, 0, false);
53889         this.scroller.dom.scrollLeft = x;
53890     },
53891
53892     /**
53893      * Focuses the specified cell.
53894      * @param {Number} row The row index
53895      * @param {Number} col The column index
53896      * @param {Boolean} hscroll false to disable horizontal scrolling
53897      */
53898     focusCell : function(row, col, hscroll)
53899     {
53900         //Roo.log('GridView.focusCell');
53901         var el = this.ensureVisible(row, col, hscroll);
53902         this.focusEl.alignTo(el, "tl-tl");
53903         if(Roo.isGecko){
53904             this.focusEl.focus();
53905         }else{
53906             this.focusEl.focus.defer(1, this.focusEl);
53907         }
53908     },
53909
53910     /**
53911      * Scrolls the specified cell into view
53912      * @param {Number} row The row index
53913      * @param {Number} col The column index
53914      * @param {Boolean} hscroll false to disable horizontal scrolling
53915      */
53916     ensureVisible : function(row, col, hscroll)
53917     {
53918         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53919         //return null; //disable for testing.
53920         if(typeof row != "number"){
53921             row = row.rowIndex;
53922         }
53923         if(row < 0 && row >= this.ds.getCount()){
53924             return  null;
53925         }
53926         col = (col !== undefined ? col : 0);
53927         var cm = this.grid.colModel;
53928         while(cm.isHidden(col)){
53929             col++;
53930         }
53931
53932         var el = this.getCell(row, col);
53933         if(!el){
53934             return null;
53935         }
53936         var c = this.scroller.dom;
53937
53938         var ctop = parseInt(el.offsetTop, 10);
53939         var cleft = parseInt(el.offsetLeft, 10);
53940         var cbot = ctop + el.offsetHeight;
53941         var cright = cleft + el.offsetWidth;
53942         
53943         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53944         var stop = parseInt(c.scrollTop, 10);
53945         var sleft = parseInt(c.scrollLeft, 10);
53946         var sbot = stop + ch;
53947         var sright = sleft + c.clientWidth;
53948         /*
53949         Roo.log('GridView.ensureVisible:' +
53950                 ' ctop:' + ctop +
53951                 ' c.clientHeight:' + c.clientHeight +
53952                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53953                 ' stop:' + stop +
53954                 ' cbot:' + cbot +
53955                 ' sbot:' + sbot +
53956                 ' ch:' + ch  
53957                 );
53958         */
53959         if(ctop < stop){
53960              c.scrollTop = ctop;
53961             //Roo.log("set scrolltop to ctop DISABLE?");
53962         }else if(cbot > sbot){
53963             //Roo.log("set scrolltop to cbot-ch");
53964             c.scrollTop = cbot-ch;
53965         }
53966         
53967         if(hscroll !== false){
53968             if(cleft < sleft){
53969                 c.scrollLeft = cleft;
53970             }else if(cright > sright){
53971                 c.scrollLeft = cright-c.clientWidth;
53972             }
53973         }
53974          
53975         return el;
53976     },
53977
53978     updateColumns : function(){
53979         this.grid.stopEditing();
53980         var cm = this.grid.colModel, colIds = this.getColumnIds();
53981         //var totalWidth = cm.getTotalWidth();
53982         var pos = 0;
53983         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53984             //if(cm.isHidden(i)) continue;
53985             var w = cm.getColumnWidth(i);
53986             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53987             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53988         }
53989         this.updateSplitters();
53990     },
53991
53992     generateRules : function(cm){
53993         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53994         Roo.util.CSS.removeStyleSheet(rulesId);
53995         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53996             var cid = cm.getColumnId(i);
53997             var align = '';
53998             if(cm.config[i].align){
53999                 align = 'text-align:'+cm.config[i].align+';';
54000             }
54001             var hidden = '';
54002             if(cm.isHidden(i)){
54003                 hidden = 'display:none;';
54004             }
54005             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
54006             ruleBuf.push(
54007                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
54008                     this.hdSelector, cid, " {\n", align, width, "}\n",
54009                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
54010                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
54011         }
54012         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
54013     },
54014
54015     updateSplitters : function(){
54016         var cm = this.cm, s = this.getSplitters();
54017         if(s){ // splitters not created yet
54018             var pos = 0, locked = true;
54019             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54020                 if(cm.isHidden(i)) continue;
54021                 var w = cm.getColumnWidth(i); // make sure it's a number
54022                 if(!cm.isLocked(i) && locked){
54023                     pos = 0;
54024                     locked = false;
54025                 }
54026                 pos += w;
54027                 s[i].style.left = (pos-this.splitOffset) + "px";
54028             }
54029         }
54030     },
54031
54032     handleHiddenChange : function(colModel, colIndex, hidden){
54033         if(hidden){
54034             this.hideColumn(colIndex);
54035         }else{
54036             this.unhideColumn(colIndex);
54037         }
54038     },
54039
54040     hideColumn : function(colIndex){
54041         var cid = this.getColumnId(colIndex);
54042         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54043         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54044         if(Roo.isSafari){
54045             this.updateHeaders();
54046         }
54047         this.updateSplitters();
54048         this.layout();
54049     },
54050
54051     unhideColumn : function(colIndex){
54052         var cid = this.getColumnId(colIndex);
54053         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54054         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54055
54056         if(Roo.isSafari){
54057             this.updateHeaders();
54058         }
54059         this.updateSplitters();
54060         this.layout();
54061     },
54062
54063     insertRows : function(dm, firstRow, lastRow, isUpdate){
54064         if(firstRow == 0 && lastRow == dm.getCount()-1){
54065             this.refresh();
54066         }else{
54067             if(!isUpdate){
54068                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54069             }
54070             var s = this.getScrollState();
54071             var markup = this.renderRows(firstRow, lastRow);
54072             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54073             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54074             this.restoreScroll(s);
54075             if(!isUpdate){
54076                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54077                 this.syncRowHeights(firstRow, lastRow);
54078                 this.stripeRows(firstRow);
54079                 this.layout();
54080             }
54081         }
54082     },
54083
54084     bufferRows : function(markup, target, index){
54085         var before = null, trows = target.rows, tbody = target.tBodies[0];
54086         if(index < trows.length){
54087             before = trows[index];
54088         }
54089         var b = document.createElement("div");
54090         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54091         var rows = b.firstChild.rows;
54092         for(var i = 0, len = rows.length; i < len; i++){
54093             if(before){
54094                 tbody.insertBefore(rows[0], before);
54095             }else{
54096                 tbody.appendChild(rows[0]);
54097             }
54098         }
54099         b.innerHTML = "";
54100         b = null;
54101     },
54102
54103     deleteRows : function(dm, firstRow, lastRow){
54104         if(dm.getRowCount()<1){
54105             this.fireEvent("beforerefresh", this);
54106             this.mainBody.update("");
54107             this.lockedBody.update("");
54108             this.fireEvent("refresh", this);
54109         }else{
54110             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54111             var bt = this.getBodyTable();
54112             var tbody = bt.firstChild;
54113             var rows = bt.rows;
54114             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54115                 tbody.removeChild(rows[firstRow]);
54116             }
54117             this.stripeRows(firstRow);
54118             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54119         }
54120     },
54121
54122     updateRows : function(dataSource, firstRow, lastRow){
54123         var s = this.getScrollState();
54124         this.refresh();
54125         this.restoreScroll(s);
54126     },
54127
54128     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54129         if(!noRefresh){
54130            this.refresh();
54131         }
54132         this.updateHeaderSortState();
54133     },
54134
54135     getScrollState : function(){
54136         
54137         var sb = this.scroller.dom;
54138         return {left: sb.scrollLeft, top: sb.scrollTop};
54139     },
54140
54141     stripeRows : function(startRow){
54142         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54143             return;
54144         }
54145         startRow = startRow || 0;
54146         var rows = this.getBodyTable().rows;
54147         var lrows = this.getLockedTable().rows;
54148         var cls = ' x-grid-row-alt ';
54149         for(var i = startRow, len = rows.length; i < len; i++){
54150             var row = rows[i], lrow = lrows[i];
54151             var isAlt = ((i+1) % 2 == 0);
54152             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54153             if(isAlt == hasAlt){
54154                 continue;
54155             }
54156             if(isAlt){
54157                 row.className += " x-grid-row-alt";
54158             }else{
54159                 row.className = row.className.replace("x-grid-row-alt", "");
54160             }
54161             if(lrow){
54162                 lrow.className = row.className;
54163             }
54164         }
54165     },
54166
54167     restoreScroll : function(state){
54168         //Roo.log('GridView.restoreScroll');
54169         var sb = this.scroller.dom;
54170         sb.scrollLeft = state.left;
54171         sb.scrollTop = state.top;
54172         this.syncScroll();
54173     },
54174
54175     syncScroll : function(){
54176         //Roo.log('GridView.syncScroll');
54177         var sb = this.scroller.dom;
54178         var sh = this.mainHd.dom;
54179         var bs = this.mainBody.dom;
54180         var lv = this.lockedBody.dom;
54181         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54182         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54183     },
54184
54185     handleScroll : function(e){
54186         this.syncScroll();
54187         var sb = this.scroller.dom;
54188         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54189         e.stopEvent();
54190     },
54191
54192     handleWheel : function(e){
54193         var d = e.getWheelDelta();
54194         this.scroller.dom.scrollTop -= d*22;
54195         // set this here to prevent jumpy scrolling on large tables
54196         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54197         e.stopEvent();
54198     },
54199
54200     renderRows : function(startRow, endRow){
54201         // pull in all the crap needed to render rows
54202         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54203         var colCount = cm.getColumnCount();
54204
54205         if(ds.getCount() < 1){
54206             return ["", ""];
54207         }
54208
54209         // build a map for all the columns
54210         var cs = [];
54211         for(var i = 0; i < colCount; i++){
54212             var name = cm.getDataIndex(i);
54213             cs[i] = {
54214                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54215                 renderer : cm.getRenderer(i),
54216                 id : cm.getColumnId(i),
54217                 locked : cm.isLocked(i)
54218             };
54219         }
54220
54221         startRow = startRow || 0;
54222         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54223
54224         // records to render
54225         var rs = ds.getRange(startRow, endRow);
54226
54227         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54228     },
54229
54230     // As much as I hate to duplicate code, this was branched because FireFox really hates
54231     // [].join("") on strings. The performance difference was substantial enough to
54232     // branch this function
54233     doRender : Roo.isGecko ?
54234             function(cs, rs, ds, startRow, colCount, stripe){
54235                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54236                 // buffers
54237                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54238                 
54239                 var hasListener = this.grid.hasListener('rowclass');
54240                 var rowcfg = {};
54241                 for(var j = 0, len = rs.length; j < len; j++){
54242                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54243                     for(var i = 0; i < colCount; i++){
54244                         c = cs[i];
54245                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54246                         p.id = c.id;
54247                         p.css = p.attr = "";
54248                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54249                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54250                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54251                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54252                         }
54253                         var markup = ct.apply(p);
54254                         if(!c.locked){
54255                             cb+= markup;
54256                         }else{
54257                             lcb+= markup;
54258                         }
54259                     }
54260                     var alt = [];
54261                     if(stripe && ((rowIndex+1) % 2 == 0)){
54262                         alt.push("x-grid-row-alt")
54263                     }
54264                     if(r.dirty){
54265                         alt.push(  " x-grid-dirty-row");
54266                     }
54267                     rp.cells = lcb;
54268                     if(this.getRowClass){
54269                         alt.push(this.getRowClass(r, rowIndex));
54270                     }
54271                     if (hasListener) {
54272                         rowcfg = {
54273                              
54274                             record: r,
54275                             rowIndex : rowIndex,
54276                             rowClass : ''
54277                         };
54278                         this.grid.fireEvent('rowclass', this, rowcfg);
54279                         alt.push(rowcfg.rowClass);
54280                     }
54281                     rp.alt = alt.join(" ");
54282                     lbuf+= rt.apply(rp);
54283                     rp.cells = cb;
54284                     buf+=  rt.apply(rp);
54285                 }
54286                 return [lbuf, buf];
54287             } :
54288             function(cs, rs, ds, startRow, colCount, stripe){
54289                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54290                 // buffers
54291                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54292                 var hasListener = this.grid.hasListener('rowclass');
54293  
54294                 var rowcfg = {};
54295                 for(var j = 0, len = rs.length; j < len; j++){
54296                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54297                     for(var i = 0; i < colCount; i++){
54298                         c = cs[i];
54299                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54300                         p.id = c.id;
54301                         p.css = p.attr = "";
54302                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54303                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54304                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54305                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54306                         }
54307                         
54308                         var markup = ct.apply(p);
54309                         if(!c.locked){
54310                             cb[cb.length] = markup;
54311                         }else{
54312                             lcb[lcb.length] = markup;
54313                         }
54314                     }
54315                     var alt = [];
54316                     if(stripe && ((rowIndex+1) % 2 == 0)){
54317                         alt.push( "x-grid-row-alt");
54318                     }
54319                     if(r.dirty){
54320                         alt.push(" x-grid-dirty-row");
54321                     }
54322                     rp.cells = lcb;
54323                     if(this.getRowClass){
54324                         alt.push( this.getRowClass(r, rowIndex));
54325                     }
54326                     if (hasListener) {
54327                         rowcfg = {
54328                              
54329                             record: r,
54330                             rowIndex : rowIndex,
54331                             rowClass : ''
54332                         };
54333                         this.grid.fireEvent('rowclass', this, rowcfg);
54334                         alt.push(rowcfg.rowClass);
54335                     }
54336                     rp.alt = alt.join(" ");
54337                     rp.cells = lcb.join("");
54338                     lbuf[lbuf.length] = rt.apply(rp);
54339                     rp.cells = cb.join("");
54340                     buf[buf.length] =  rt.apply(rp);
54341                 }
54342                 return [lbuf.join(""), buf.join("")];
54343             },
54344
54345     renderBody : function(){
54346         var markup = this.renderRows();
54347         var bt = this.templates.body;
54348         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54349     },
54350
54351     /**
54352      * Refreshes the grid
54353      * @param {Boolean} headersToo
54354      */
54355     refresh : function(headersToo){
54356         this.fireEvent("beforerefresh", this);
54357         this.grid.stopEditing();
54358         var result = this.renderBody();
54359         this.lockedBody.update(result[0]);
54360         this.mainBody.update(result[1]);
54361         if(headersToo === true){
54362             this.updateHeaders();
54363             this.updateColumns();
54364             this.updateSplitters();
54365             this.updateHeaderSortState();
54366         }
54367         this.syncRowHeights();
54368         this.layout();
54369         this.fireEvent("refresh", this);
54370     },
54371
54372     handleColumnMove : function(cm, oldIndex, newIndex){
54373         this.indexMap = null;
54374         var s = this.getScrollState();
54375         this.refresh(true);
54376         this.restoreScroll(s);
54377         this.afterMove(newIndex);
54378     },
54379
54380     afterMove : function(colIndex){
54381         if(this.enableMoveAnim && Roo.enableFx){
54382             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54383         }
54384         // if multisort - fix sortOrder, and reload..
54385         if (this.grid.dataSource.multiSort) {
54386             // the we can call sort again..
54387             var dm = this.grid.dataSource;
54388             var cm = this.grid.colModel;
54389             var so = [];
54390             for(var i = 0; i < cm.config.length; i++ ) {
54391                 
54392                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54393                     continue; // dont' bother, it's not in sort list or being set.
54394                 }
54395                 
54396                 so.push(cm.config[i].dataIndex);
54397             };
54398             dm.sortOrder = so;
54399             dm.load(dm.lastOptions);
54400             
54401             
54402         }
54403         
54404     },
54405
54406     updateCell : function(dm, rowIndex, dataIndex){
54407         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54408         if(typeof colIndex == "undefined"){ // not present in grid
54409             return;
54410         }
54411         var cm = this.grid.colModel;
54412         var cell = this.getCell(rowIndex, colIndex);
54413         var cellText = this.getCellText(rowIndex, colIndex);
54414
54415         var p = {
54416             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54417             id : cm.getColumnId(colIndex),
54418             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54419         };
54420         var renderer = cm.getRenderer(colIndex);
54421         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54422         if(typeof val == "undefined" || val === "") val = "&#160;";
54423         cellText.innerHTML = val;
54424         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54425         this.syncRowHeights(rowIndex, rowIndex);
54426     },
54427
54428     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54429         var maxWidth = 0;
54430         if(this.grid.autoSizeHeaders){
54431             var h = this.getHeaderCellMeasure(colIndex);
54432             maxWidth = Math.max(maxWidth, h.scrollWidth);
54433         }
54434         var tb, index;
54435         if(this.cm.isLocked(colIndex)){
54436             tb = this.getLockedTable();
54437             index = colIndex;
54438         }else{
54439             tb = this.getBodyTable();
54440             index = colIndex - this.cm.getLockedCount();
54441         }
54442         if(tb && tb.rows){
54443             var rows = tb.rows;
54444             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54445             for(var i = 0; i < stopIndex; i++){
54446                 var cell = rows[i].childNodes[index].firstChild;
54447                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54448             }
54449         }
54450         return maxWidth + /*margin for error in IE*/ 5;
54451     },
54452     /**
54453      * Autofit a column to its content.
54454      * @param {Number} colIndex
54455      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54456      */
54457      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54458          if(this.cm.isHidden(colIndex)){
54459              return; // can't calc a hidden column
54460          }
54461         if(forceMinSize){
54462             var cid = this.cm.getColumnId(colIndex);
54463             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54464            if(this.grid.autoSizeHeaders){
54465                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54466            }
54467         }
54468         var newWidth = this.calcColumnWidth(colIndex);
54469         this.cm.setColumnWidth(colIndex,
54470             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54471         if(!suppressEvent){
54472             this.grid.fireEvent("columnresize", colIndex, newWidth);
54473         }
54474     },
54475
54476     /**
54477      * Autofits all columns to their content and then expands to fit any extra space in the grid
54478      */
54479      autoSizeColumns : function(){
54480         var cm = this.grid.colModel;
54481         var colCount = cm.getColumnCount();
54482         for(var i = 0; i < colCount; i++){
54483             this.autoSizeColumn(i, true, true);
54484         }
54485         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54486             this.fitColumns();
54487         }else{
54488             this.updateColumns();
54489             this.layout();
54490         }
54491     },
54492
54493     /**
54494      * Autofits all columns to the grid's width proportionate with their current size
54495      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54496      */
54497     fitColumns : function(reserveScrollSpace){
54498         var cm = this.grid.colModel;
54499         var colCount = cm.getColumnCount();
54500         var cols = [];
54501         var width = 0;
54502         var i, w;
54503         for (i = 0; i < colCount; i++){
54504             if(!cm.isHidden(i) && !cm.isFixed(i)){
54505                 w = cm.getColumnWidth(i);
54506                 cols.push(i);
54507                 cols.push(w);
54508                 width += w;
54509             }
54510         }
54511         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54512         if(reserveScrollSpace){
54513             avail -= 17;
54514         }
54515         var frac = (avail - cm.getTotalWidth())/width;
54516         while (cols.length){
54517             w = cols.pop();
54518             i = cols.pop();
54519             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54520         }
54521         this.updateColumns();
54522         this.layout();
54523     },
54524
54525     onRowSelect : function(rowIndex){
54526         var row = this.getRowComposite(rowIndex);
54527         row.addClass("x-grid-row-selected");
54528     },
54529
54530     onRowDeselect : function(rowIndex){
54531         var row = this.getRowComposite(rowIndex);
54532         row.removeClass("x-grid-row-selected");
54533     },
54534
54535     onCellSelect : function(row, col){
54536         var cell = this.getCell(row, col);
54537         if(cell){
54538             Roo.fly(cell).addClass("x-grid-cell-selected");
54539         }
54540     },
54541
54542     onCellDeselect : function(row, col){
54543         var cell = this.getCell(row, col);
54544         if(cell){
54545             Roo.fly(cell).removeClass("x-grid-cell-selected");
54546         }
54547     },
54548
54549     updateHeaderSortState : function(){
54550         
54551         // sort state can be single { field: xxx, direction : yyy}
54552         // or   { xxx=>ASC , yyy : DESC ..... }
54553         
54554         var mstate = {};
54555         if (!this.ds.multiSort) { 
54556             var state = this.ds.getSortState();
54557             if(!state){
54558                 return;
54559             }
54560             mstate[state.field] = state.direction;
54561             // FIXME... - this is not used here.. but might be elsewhere..
54562             this.sortState = state;
54563             
54564         } else {
54565             mstate = this.ds.sortToggle;
54566         }
54567         //remove existing sort classes..
54568         
54569         var sc = this.sortClasses;
54570         var hds = this.el.select(this.headerSelector).removeClass(sc);
54571         
54572         for(var f in mstate) {
54573         
54574             var sortColumn = this.cm.findColumnIndex(f);
54575             
54576             if(sortColumn != -1){
54577                 var sortDir = mstate[f];        
54578                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54579             }
54580         }
54581         
54582          
54583         
54584     },
54585
54586
54587     handleHeaderClick : function(g, index,e){
54588         
54589         Roo.log("header click");
54590         
54591         if (Roo.isTouch) {
54592             // touch events on header are handled by context
54593             this.handleHdCtx(g,index,e);
54594             return;
54595         }
54596         
54597         
54598         if(this.headersDisabled){
54599             return;
54600         }
54601         var dm = g.dataSource, cm = g.colModel;
54602         if(!cm.isSortable(index)){
54603             return;
54604         }
54605         g.stopEditing();
54606         
54607         if (dm.multiSort) {
54608             // update the sortOrder
54609             var so = [];
54610             for(var i = 0; i < cm.config.length; i++ ) {
54611                 
54612                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54613                     continue; // dont' bother, it's not in sort list or being set.
54614                 }
54615                 
54616                 so.push(cm.config[i].dataIndex);
54617             };
54618             dm.sortOrder = so;
54619         }
54620         
54621         
54622         dm.sort(cm.getDataIndex(index));
54623     },
54624
54625
54626     destroy : function(){
54627         if(this.colMenu){
54628             this.colMenu.removeAll();
54629             Roo.menu.MenuMgr.unregister(this.colMenu);
54630             this.colMenu.getEl().remove();
54631             delete this.colMenu;
54632         }
54633         if(this.hmenu){
54634             this.hmenu.removeAll();
54635             Roo.menu.MenuMgr.unregister(this.hmenu);
54636             this.hmenu.getEl().remove();
54637             delete this.hmenu;
54638         }
54639         if(this.grid.enableColumnMove){
54640             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54641             if(dds){
54642                 for(var dd in dds){
54643                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54644                         var elid = dds[dd].dragElId;
54645                         dds[dd].unreg();
54646                         Roo.get(elid).remove();
54647                     } else if(dds[dd].config.isTarget){
54648                         dds[dd].proxyTop.remove();
54649                         dds[dd].proxyBottom.remove();
54650                         dds[dd].unreg();
54651                     }
54652                     if(Roo.dd.DDM.locationCache[dd]){
54653                         delete Roo.dd.DDM.locationCache[dd];
54654                     }
54655                 }
54656                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54657             }
54658         }
54659         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54660         this.bind(null, null);
54661         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54662     },
54663
54664     handleLockChange : function(){
54665         this.refresh(true);
54666     },
54667
54668     onDenyColumnLock : function(){
54669
54670     },
54671
54672     onDenyColumnHide : function(){
54673
54674     },
54675
54676     handleHdMenuClick : function(item){
54677         var index = this.hdCtxIndex;
54678         var cm = this.cm, ds = this.ds;
54679         switch(item.id){
54680             case "asc":
54681                 ds.sort(cm.getDataIndex(index), "ASC");
54682                 break;
54683             case "desc":
54684                 ds.sort(cm.getDataIndex(index), "DESC");
54685                 break;
54686             case "lock":
54687                 var lc = cm.getLockedCount();
54688                 if(cm.getColumnCount(true) <= lc+1){
54689                     this.onDenyColumnLock();
54690                     return;
54691                 }
54692                 if(lc != index){
54693                     cm.setLocked(index, true, true);
54694                     cm.moveColumn(index, lc);
54695                     this.grid.fireEvent("columnmove", index, lc);
54696                 }else{
54697                     cm.setLocked(index, true);
54698                 }
54699             break;
54700             case "unlock":
54701                 var lc = cm.getLockedCount();
54702                 if((lc-1) != index){
54703                     cm.setLocked(index, false, true);
54704                     cm.moveColumn(index, lc-1);
54705                     this.grid.fireEvent("columnmove", index, lc-1);
54706                 }else{
54707                     cm.setLocked(index, false);
54708                 }
54709             break;
54710             case 'wider': // used to expand cols on touch..
54711             case 'narrow':
54712                 var cw = cm.getColumnWidth(index);
54713                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54714                 cw = Math.max(0, cw);
54715                 cw = Math.min(cw,4000);
54716                 cm.setColumnWidth(index, cw);
54717                 break;
54718                 
54719             default:
54720                 index = cm.getIndexById(item.id.substr(4));
54721                 if(index != -1){
54722                     if(item.checked && cm.getColumnCount(true) <= 1){
54723                         this.onDenyColumnHide();
54724                         return false;
54725                     }
54726                     cm.setHidden(index, item.checked);
54727                 }
54728         }
54729         return true;
54730     },
54731
54732     beforeColMenuShow : function(){
54733         var cm = this.cm,  colCount = cm.getColumnCount();
54734         this.colMenu.removeAll();
54735         for(var i = 0; i < colCount; i++){
54736             this.colMenu.add(new Roo.menu.CheckItem({
54737                 id: "col-"+cm.getColumnId(i),
54738                 text: cm.getColumnHeader(i),
54739                 checked: !cm.isHidden(i),
54740                 hideOnClick:false
54741             }));
54742         }
54743     },
54744
54745     handleHdCtx : function(g, index, e){
54746         e.stopEvent();
54747         var hd = this.getHeaderCell(index);
54748         this.hdCtxIndex = index;
54749         var ms = this.hmenu.items, cm = this.cm;
54750         ms.get("asc").setDisabled(!cm.isSortable(index));
54751         ms.get("desc").setDisabled(!cm.isSortable(index));
54752         if(this.grid.enableColLock !== false){
54753             ms.get("lock").setDisabled(cm.isLocked(index));
54754             ms.get("unlock").setDisabled(!cm.isLocked(index));
54755         }
54756         this.hmenu.show(hd, "tl-bl");
54757     },
54758
54759     handleHdOver : function(e){
54760         var hd = this.findHeaderCell(e.getTarget());
54761         if(hd && !this.headersDisabled){
54762             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54763                this.fly(hd).addClass("x-grid-hd-over");
54764             }
54765         }
54766     },
54767
54768     handleHdOut : function(e){
54769         var hd = this.findHeaderCell(e.getTarget());
54770         if(hd){
54771             this.fly(hd).removeClass("x-grid-hd-over");
54772         }
54773     },
54774
54775     handleSplitDblClick : function(e, t){
54776         var i = this.getCellIndex(t);
54777         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54778             this.autoSizeColumn(i, true);
54779             this.layout();
54780         }
54781     },
54782
54783     render : function(){
54784
54785         var cm = this.cm;
54786         var colCount = cm.getColumnCount();
54787
54788         if(this.grid.monitorWindowResize === true){
54789             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54790         }
54791         var header = this.renderHeaders();
54792         var body = this.templates.body.apply({rows:""});
54793         var html = this.templates.master.apply({
54794             lockedBody: body,
54795             body: body,
54796             lockedHeader: header[0],
54797             header: header[1]
54798         });
54799
54800         //this.updateColumns();
54801
54802         this.grid.getGridEl().dom.innerHTML = html;
54803
54804         this.initElements();
54805         
54806         // a kludge to fix the random scolling effect in webkit
54807         this.el.on("scroll", function() {
54808             this.el.dom.scrollTop=0; // hopefully not recursive..
54809         },this);
54810
54811         this.scroller.on("scroll", this.handleScroll, this);
54812         this.lockedBody.on("mousewheel", this.handleWheel, this);
54813         this.mainBody.on("mousewheel", this.handleWheel, this);
54814
54815         this.mainHd.on("mouseover", this.handleHdOver, this);
54816         this.mainHd.on("mouseout", this.handleHdOut, this);
54817         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54818                 {delegate: "."+this.splitClass});
54819
54820         this.lockedHd.on("mouseover", this.handleHdOver, this);
54821         this.lockedHd.on("mouseout", this.handleHdOut, this);
54822         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54823                 {delegate: "."+this.splitClass});
54824
54825         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54826             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54827         }
54828
54829         this.updateSplitters();
54830
54831         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54832             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54833             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54834         }
54835
54836         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54837             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54838             this.hmenu.add(
54839                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54840                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54841             );
54842             if(this.grid.enableColLock !== false){
54843                 this.hmenu.add('-',
54844                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54845                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54846                 );
54847             }
54848             if (Roo.isTouch) {
54849                  this.hmenu.add('-',
54850                     {id:"wider", text: this.columnsWiderText},
54851                     {id:"narrow", text: this.columnsNarrowText }
54852                 );
54853                 
54854                  
54855             }
54856             
54857             if(this.grid.enableColumnHide !== false){
54858
54859                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54860                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54861                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54862
54863                 this.hmenu.add('-',
54864                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54865                 );
54866             }
54867             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54868
54869             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54870         }
54871
54872         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54873             this.dd = new Roo.grid.GridDragZone(this.grid, {
54874                 ddGroup : this.grid.ddGroup || 'GridDD'
54875             });
54876             
54877         }
54878
54879         /*
54880         for(var i = 0; i < colCount; i++){
54881             if(cm.isHidden(i)){
54882                 this.hideColumn(i);
54883             }
54884             if(cm.config[i].align){
54885                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54886                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54887             }
54888         }*/
54889         
54890         this.updateHeaderSortState();
54891
54892         this.beforeInitialResize();
54893         this.layout(true);
54894
54895         // two part rendering gives faster view to the user
54896         this.renderPhase2.defer(1, this);
54897     },
54898
54899     renderPhase2 : function(){
54900         // render the rows now
54901         this.refresh();
54902         if(this.grid.autoSizeColumns){
54903             this.autoSizeColumns();
54904         }
54905     },
54906
54907     beforeInitialResize : function(){
54908
54909     },
54910
54911     onColumnSplitterMoved : function(i, w){
54912         this.userResized = true;
54913         var cm = this.grid.colModel;
54914         cm.setColumnWidth(i, w, true);
54915         var cid = cm.getColumnId(i);
54916         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54917         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54918         this.updateSplitters();
54919         this.layout();
54920         this.grid.fireEvent("columnresize", i, w);
54921     },
54922
54923     syncRowHeights : function(startIndex, endIndex){
54924         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54925             startIndex = startIndex || 0;
54926             var mrows = this.getBodyTable().rows;
54927             var lrows = this.getLockedTable().rows;
54928             var len = mrows.length-1;
54929             endIndex = Math.min(endIndex || len, len);
54930             for(var i = startIndex; i <= endIndex; i++){
54931                 var m = mrows[i], l = lrows[i];
54932                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54933                 m.style.height = l.style.height = h + "px";
54934             }
54935         }
54936     },
54937
54938     layout : function(initialRender, is2ndPass){
54939         var g = this.grid;
54940         var auto = g.autoHeight;
54941         var scrollOffset = 16;
54942         var c = g.getGridEl(), cm = this.cm,
54943                 expandCol = g.autoExpandColumn,
54944                 gv = this;
54945         //c.beginMeasure();
54946
54947         if(!c.dom.offsetWidth){ // display:none?
54948             if(initialRender){
54949                 this.lockedWrap.show();
54950                 this.mainWrap.show();
54951             }
54952             return;
54953         }
54954
54955         var hasLock = this.cm.isLocked(0);
54956
54957         var tbh = this.headerPanel.getHeight();
54958         var bbh = this.footerPanel.getHeight();
54959
54960         if(auto){
54961             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54962             var newHeight = ch + c.getBorderWidth("tb");
54963             if(g.maxHeight){
54964                 newHeight = Math.min(g.maxHeight, newHeight);
54965             }
54966             c.setHeight(newHeight);
54967         }
54968
54969         if(g.autoWidth){
54970             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54971         }
54972
54973         var s = this.scroller;
54974
54975         var csize = c.getSize(true);
54976
54977         this.el.setSize(csize.width, csize.height);
54978
54979         this.headerPanel.setWidth(csize.width);
54980         this.footerPanel.setWidth(csize.width);
54981
54982         var hdHeight = this.mainHd.getHeight();
54983         var vw = csize.width;
54984         var vh = csize.height - (tbh + bbh);
54985
54986         s.setSize(vw, vh);
54987
54988         var bt = this.getBodyTable();
54989         var ltWidth = hasLock ?
54990                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54991
54992         var scrollHeight = bt.offsetHeight;
54993         var scrollWidth = ltWidth + bt.offsetWidth;
54994         var vscroll = false, hscroll = false;
54995
54996         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54997
54998         var lw = this.lockedWrap, mw = this.mainWrap;
54999         var lb = this.lockedBody, mb = this.mainBody;
55000
55001         setTimeout(function(){
55002             var t = s.dom.offsetTop;
55003             var w = s.dom.clientWidth,
55004                 h = s.dom.clientHeight;
55005
55006             lw.setTop(t);
55007             lw.setSize(ltWidth, h);
55008
55009             mw.setLeftTop(ltWidth, t);
55010             mw.setSize(w-ltWidth, h);
55011
55012             lb.setHeight(h-hdHeight);
55013             mb.setHeight(h-hdHeight);
55014
55015             if(is2ndPass !== true && !gv.userResized && expandCol){
55016                 // high speed resize without full column calculation
55017                 
55018                 var ci = cm.getIndexById(expandCol);
55019                 if (ci < 0) {
55020                     ci = cm.findColumnIndex(expandCol);
55021                 }
55022                 ci = Math.max(0, ci); // make sure it's got at least the first col.
55023                 var expandId = cm.getColumnId(ci);
55024                 var  tw = cm.getTotalWidth(false);
55025                 var currentWidth = cm.getColumnWidth(ci);
55026                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
55027                 if(currentWidth != cw){
55028                     cm.setColumnWidth(ci, cw, true);
55029                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55030                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55031                     gv.updateSplitters();
55032                     gv.layout(false, true);
55033                 }
55034             }
55035
55036             if(initialRender){
55037                 lw.show();
55038                 mw.show();
55039             }
55040             //c.endMeasure();
55041         }, 10);
55042     },
55043
55044     onWindowResize : function(){
55045         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55046             return;
55047         }
55048         this.layout();
55049     },
55050
55051     appendFooter : function(parentEl){
55052         return null;
55053     },
55054
55055     sortAscText : "Sort Ascending",
55056     sortDescText : "Sort Descending",
55057     lockText : "Lock Column",
55058     unlockText : "Unlock Column",
55059     columnsText : "Columns",
55060  
55061     columnsWiderText : "Wider",
55062     columnsNarrowText : "Thinner"
55063 });
55064
55065
55066 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55067     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55068     this.proxy.el.addClass('x-grid3-col-dd');
55069 };
55070
55071 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55072     handleMouseDown : function(e){
55073
55074     },
55075
55076     callHandleMouseDown : function(e){
55077         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55078     }
55079 });
55080 /*
55081  * Based on:
55082  * Ext JS Library 1.1.1
55083  * Copyright(c) 2006-2007, Ext JS, LLC.
55084  *
55085  * Originally Released Under LGPL - original licence link has changed is not relivant.
55086  *
55087  * Fork - LGPL
55088  * <script type="text/javascript">
55089  */
55090  
55091 // private
55092 // This is a support class used internally by the Grid components
55093 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55094     this.grid = grid;
55095     this.view = grid.getView();
55096     this.proxy = this.view.resizeProxy;
55097     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55098         "gridSplitters" + this.grid.getGridEl().id, {
55099         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55100     });
55101     this.setHandleElId(Roo.id(hd));
55102     this.setOuterHandleElId(Roo.id(hd2));
55103     this.scroll = false;
55104 };
55105 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55106     fly: Roo.Element.fly,
55107
55108     b4StartDrag : function(x, y){
55109         this.view.headersDisabled = true;
55110         this.proxy.setHeight(this.view.mainWrap.getHeight());
55111         var w = this.cm.getColumnWidth(this.cellIndex);
55112         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55113         this.resetConstraints();
55114         this.setXConstraint(minw, 1000);
55115         this.setYConstraint(0, 0);
55116         this.minX = x - minw;
55117         this.maxX = x + 1000;
55118         this.startPos = x;
55119         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55120     },
55121
55122
55123     handleMouseDown : function(e){
55124         ev = Roo.EventObject.setEvent(e);
55125         var t = this.fly(ev.getTarget());
55126         if(t.hasClass("x-grid-split")){
55127             this.cellIndex = this.view.getCellIndex(t.dom);
55128             this.split = t.dom;
55129             this.cm = this.grid.colModel;
55130             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55131                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55132             }
55133         }
55134     },
55135
55136     endDrag : function(e){
55137         this.view.headersDisabled = false;
55138         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55139         var diff = endX - this.startPos;
55140         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55141     },
55142
55143     autoOffset : function(){
55144         this.setDelta(0,0);
55145     }
55146 });/*
55147  * Based on:
55148  * Ext JS Library 1.1.1
55149  * Copyright(c) 2006-2007, Ext JS, LLC.
55150  *
55151  * Originally Released Under LGPL - original licence link has changed is not relivant.
55152  *
55153  * Fork - LGPL
55154  * <script type="text/javascript">
55155  */
55156  
55157 // private
55158 // This is a support class used internally by the Grid components
55159 Roo.grid.GridDragZone = function(grid, config){
55160     this.view = grid.getView();
55161     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55162     if(this.view.lockedBody){
55163         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55164         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55165     }
55166     this.scroll = false;
55167     this.grid = grid;
55168     this.ddel = document.createElement('div');
55169     this.ddel.className = 'x-grid-dd-wrap';
55170 };
55171
55172 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55173     ddGroup : "GridDD",
55174
55175     getDragData : function(e){
55176         var t = Roo.lib.Event.getTarget(e);
55177         var rowIndex = this.view.findRowIndex(t);
55178         var sm = this.grid.selModel;
55179             
55180         //Roo.log(rowIndex);
55181         
55182         if (sm.getSelectedCell) {
55183             // cell selection..
55184             if (!sm.getSelectedCell()) {
55185                 return false;
55186             }
55187             if (rowIndex != sm.getSelectedCell()[0]) {
55188                 return false;
55189             }
55190         
55191         }
55192         
55193         if(rowIndex !== false){
55194             
55195             // if editorgrid.. 
55196             
55197             
55198             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55199                
55200             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55201               //  
55202             //}
55203             if (e.hasModifier()){
55204                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55205             }
55206             
55207             Roo.log("getDragData");
55208             
55209             return {
55210                 grid: this.grid,
55211                 ddel: this.ddel,
55212                 rowIndex: rowIndex,
55213                 selections:sm.getSelections ? sm.getSelections() : (
55214                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55215                 )
55216             };
55217         }
55218         return false;
55219     },
55220
55221     onInitDrag : function(e){
55222         var data = this.dragData;
55223         this.ddel.innerHTML = this.grid.getDragDropText();
55224         this.proxy.update(this.ddel);
55225         // fire start drag?
55226     },
55227
55228     afterRepair : function(){
55229         this.dragging = false;
55230     },
55231
55232     getRepairXY : function(e, data){
55233         return false;
55234     },
55235
55236     onEndDrag : function(data, e){
55237         // fire end drag?
55238     },
55239
55240     onValidDrop : function(dd, e, id){
55241         // fire drag drop?
55242         this.hideProxy();
55243     },
55244
55245     beforeInvalidDrop : function(e, id){
55246
55247     }
55248 });/*
55249  * Based on:
55250  * Ext JS Library 1.1.1
55251  * Copyright(c) 2006-2007, Ext JS, LLC.
55252  *
55253  * Originally Released Under LGPL - original licence link has changed is not relivant.
55254  *
55255  * Fork - LGPL
55256  * <script type="text/javascript">
55257  */
55258  
55259
55260 /**
55261  * @class Roo.grid.ColumnModel
55262  * @extends Roo.util.Observable
55263  * This is the default implementation of a ColumnModel used by the Grid. It defines
55264  * the columns in the grid.
55265  * <br>Usage:<br>
55266  <pre><code>
55267  var colModel = new Roo.grid.ColumnModel([
55268         {header: "Ticker", width: 60, sortable: true, locked: true},
55269         {header: "Company Name", width: 150, sortable: true},
55270         {header: "Market Cap.", width: 100, sortable: true},
55271         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55272         {header: "Employees", width: 100, sortable: true, resizable: false}
55273  ]);
55274  </code></pre>
55275  * <p>
55276  
55277  * The config options listed for this class are options which may appear in each
55278  * individual column definition.
55279  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55280  * @constructor
55281  * @param {Object} config An Array of column config objects. See this class's
55282  * config objects for details.
55283 */
55284 Roo.grid.ColumnModel = function(config){
55285         /**
55286      * The config passed into the constructor
55287      */
55288     this.config = config;
55289     this.lookup = {};
55290
55291     // if no id, create one
55292     // if the column does not have a dataIndex mapping,
55293     // map it to the order it is in the config
55294     for(var i = 0, len = config.length; i < len; i++){
55295         var c = config[i];
55296         if(typeof c.dataIndex == "undefined"){
55297             c.dataIndex = i;
55298         }
55299         if(typeof c.renderer == "string"){
55300             c.renderer = Roo.util.Format[c.renderer];
55301         }
55302         if(typeof c.id == "undefined"){
55303             c.id = Roo.id();
55304         }
55305         if(c.editor && c.editor.xtype){
55306             c.editor  = Roo.factory(c.editor, Roo.grid);
55307         }
55308         if(c.editor && c.editor.isFormField){
55309             c.editor = new Roo.grid.GridEditor(c.editor);
55310         }
55311         this.lookup[c.id] = c;
55312     }
55313
55314     /**
55315      * The width of columns which have no width specified (defaults to 100)
55316      * @type Number
55317      */
55318     this.defaultWidth = 100;
55319
55320     /**
55321      * Default sortable of columns which have no sortable specified (defaults to false)
55322      * @type Boolean
55323      */
55324     this.defaultSortable = false;
55325
55326     this.addEvents({
55327         /**
55328              * @event widthchange
55329              * Fires when the width of a column changes.
55330              * @param {ColumnModel} this
55331              * @param {Number} columnIndex The column index
55332              * @param {Number} newWidth The new width
55333              */
55334             "widthchange": true,
55335         /**
55336              * @event headerchange
55337              * Fires when the text of a header changes.
55338              * @param {ColumnModel} this
55339              * @param {Number} columnIndex The column index
55340              * @param {Number} newText The new header text
55341              */
55342             "headerchange": true,
55343         /**
55344              * @event hiddenchange
55345              * Fires when a column is hidden or "unhidden".
55346              * @param {ColumnModel} this
55347              * @param {Number} columnIndex The column index
55348              * @param {Boolean} hidden true if hidden, false otherwise
55349              */
55350             "hiddenchange": true,
55351             /**
55352          * @event columnmoved
55353          * Fires when a column is moved.
55354          * @param {ColumnModel} this
55355          * @param {Number} oldIndex
55356          * @param {Number} newIndex
55357          */
55358         "columnmoved" : true,
55359         /**
55360          * @event columlockchange
55361          * Fires when a column's locked state is changed
55362          * @param {ColumnModel} this
55363          * @param {Number} colIndex
55364          * @param {Boolean} locked true if locked
55365          */
55366         "columnlockchange" : true
55367     });
55368     Roo.grid.ColumnModel.superclass.constructor.call(this);
55369 };
55370 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55371     /**
55372      * @cfg {String} header The header text to display in the Grid view.
55373      */
55374     /**
55375      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55376      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55377      * specified, the column's index is used as an index into the Record's data Array.
55378      */
55379     /**
55380      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55381      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55382      */
55383     /**
55384      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55385      * Defaults to the value of the {@link #defaultSortable} property.
55386      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55387      */
55388     /**
55389      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55390      */
55391     /**
55392      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55393      */
55394     /**
55395      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55396      */
55397     /**
55398      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55399      */
55400     /**
55401      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55402      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55403      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55404      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55405      */
55406        /**
55407      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55408      */
55409     /**
55410      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55411      */
55412     /**
55413      * @cfg {String} cursor (Optional)
55414      */
55415     /**
55416      * @cfg {String} tooltip (Optional)
55417      */
55418     /**
55419      * @cfg {Number} xs (Optional)
55420      */
55421     /**
55422      * @cfg {Number} sm (Optional)
55423      */
55424     /**
55425      * @cfg {Number} md (Optional)
55426      */
55427     /**
55428      * @cfg {Number} lg (Optional)
55429      */
55430     /**
55431      * Returns the id of the column at the specified index.
55432      * @param {Number} index The column index
55433      * @return {String} the id
55434      */
55435     getColumnId : function(index){
55436         return this.config[index].id;
55437     },
55438
55439     /**
55440      * Returns the column for a specified id.
55441      * @param {String} id The column id
55442      * @return {Object} the column
55443      */
55444     getColumnById : function(id){
55445         return this.lookup[id];
55446     },
55447
55448     
55449     /**
55450      * Returns the column for a specified dataIndex.
55451      * @param {String} dataIndex The column dataIndex
55452      * @return {Object|Boolean} the column or false if not found
55453      */
55454     getColumnByDataIndex: function(dataIndex){
55455         var index = this.findColumnIndex(dataIndex);
55456         return index > -1 ? this.config[index] : false;
55457     },
55458     
55459     /**
55460      * Returns the index for a specified column id.
55461      * @param {String} id The column id
55462      * @return {Number} the index, or -1 if not found
55463      */
55464     getIndexById : function(id){
55465         for(var i = 0, len = this.config.length; i < len; i++){
55466             if(this.config[i].id == id){
55467                 return i;
55468             }
55469         }
55470         return -1;
55471     },
55472     
55473     /**
55474      * Returns the index for a specified column dataIndex.
55475      * @param {String} dataIndex The column dataIndex
55476      * @return {Number} the index, or -1 if not found
55477      */
55478     
55479     findColumnIndex : function(dataIndex){
55480         for(var i = 0, len = this.config.length; i < len; i++){
55481             if(this.config[i].dataIndex == dataIndex){
55482                 return i;
55483             }
55484         }
55485         return -1;
55486     },
55487     
55488     
55489     moveColumn : function(oldIndex, newIndex){
55490         var c = this.config[oldIndex];
55491         this.config.splice(oldIndex, 1);
55492         this.config.splice(newIndex, 0, c);
55493         this.dataMap = null;
55494         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55495     },
55496
55497     isLocked : function(colIndex){
55498         return this.config[colIndex].locked === true;
55499     },
55500
55501     setLocked : function(colIndex, value, suppressEvent){
55502         if(this.isLocked(colIndex) == value){
55503             return;
55504         }
55505         this.config[colIndex].locked = value;
55506         if(!suppressEvent){
55507             this.fireEvent("columnlockchange", this, colIndex, value);
55508         }
55509     },
55510
55511     getTotalLockedWidth : function(){
55512         var totalWidth = 0;
55513         for(var i = 0; i < this.config.length; i++){
55514             if(this.isLocked(i) && !this.isHidden(i)){
55515                 this.totalWidth += this.getColumnWidth(i);
55516             }
55517         }
55518         return totalWidth;
55519     },
55520
55521     getLockedCount : function(){
55522         for(var i = 0, len = this.config.length; i < len; i++){
55523             if(!this.isLocked(i)){
55524                 return i;
55525             }
55526         }
55527     },
55528
55529     /**
55530      * Returns the number of columns.
55531      * @return {Number}
55532      */
55533     getColumnCount : function(visibleOnly){
55534         if(visibleOnly === true){
55535             var c = 0;
55536             for(var i = 0, len = this.config.length; i < len; i++){
55537                 if(!this.isHidden(i)){
55538                     c++;
55539                 }
55540             }
55541             return c;
55542         }
55543         return this.config.length;
55544     },
55545
55546     /**
55547      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55548      * @param {Function} fn
55549      * @param {Object} scope (optional)
55550      * @return {Array} result
55551      */
55552     getColumnsBy : function(fn, scope){
55553         var r = [];
55554         for(var i = 0, len = this.config.length; i < len; i++){
55555             var c = this.config[i];
55556             if(fn.call(scope||this, c, i) === true){
55557                 r[r.length] = c;
55558             }
55559         }
55560         return r;
55561     },
55562
55563     /**
55564      * Returns true if the specified column is sortable.
55565      * @param {Number} col The column index
55566      * @return {Boolean}
55567      */
55568     isSortable : function(col){
55569         if(typeof this.config[col].sortable == "undefined"){
55570             return this.defaultSortable;
55571         }
55572         return this.config[col].sortable;
55573     },
55574
55575     /**
55576      * Returns the rendering (formatting) function defined for the column.
55577      * @param {Number} col The column index.
55578      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55579      */
55580     getRenderer : function(col){
55581         if(!this.config[col].renderer){
55582             return Roo.grid.ColumnModel.defaultRenderer;
55583         }
55584         return this.config[col].renderer;
55585     },
55586
55587     /**
55588      * Sets the rendering (formatting) function for a column.
55589      * @param {Number} col The column index
55590      * @param {Function} fn The function to use to process the cell's raw data
55591      * to return HTML markup for the grid view. The render function is called with
55592      * the following parameters:<ul>
55593      * <li>Data value.</li>
55594      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55595      * <li>css A CSS style string to apply to the table cell.</li>
55596      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55597      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55598      * <li>Row index</li>
55599      * <li>Column index</li>
55600      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55601      */
55602     setRenderer : function(col, fn){
55603         this.config[col].renderer = fn;
55604     },
55605
55606     /**
55607      * Returns the width for the specified column.
55608      * @param {Number} col The column index
55609      * @return {Number}
55610      */
55611     getColumnWidth : function(col){
55612         return this.config[col].width * 1 || this.defaultWidth;
55613     },
55614
55615     /**
55616      * Sets the width for a column.
55617      * @param {Number} col The column index
55618      * @param {Number} width The new width
55619      */
55620     setColumnWidth : function(col, width, suppressEvent){
55621         this.config[col].width = width;
55622         this.totalWidth = null;
55623         if(!suppressEvent){
55624              this.fireEvent("widthchange", this, col, width);
55625         }
55626     },
55627
55628     /**
55629      * Returns the total width of all columns.
55630      * @param {Boolean} includeHidden True to include hidden column widths
55631      * @return {Number}
55632      */
55633     getTotalWidth : function(includeHidden){
55634         if(!this.totalWidth){
55635             this.totalWidth = 0;
55636             for(var i = 0, len = this.config.length; i < len; i++){
55637                 if(includeHidden || !this.isHidden(i)){
55638                     this.totalWidth += this.getColumnWidth(i);
55639                 }
55640             }
55641         }
55642         return this.totalWidth;
55643     },
55644
55645     /**
55646      * Returns the header for the specified column.
55647      * @param {Number} col The column index
55648      * @return {String}
55649      */
55650     getColumnHeader : function(col){
55651         return this.config[col].header;
55652     },
55653
55654     /**
55655      * Sets the header for a column.
55656      * @param {Number} col The column index
55657      * @param {String} header The new header
55658      */
55659     setColumnHeader : function(col, header){
55660         this.config[col].header = header;
55661         this.fireEvent("headerchange", this, col, header);
55662     },
55663
55664     /**
55665      * Returns the tooltip for the specified column.
55666      * @param {Number} col The column index
55667      * @return {String}
55668      */
55669     getColumnTooltip : function(col){
55670             return this.config[col].tooltip;
55671     },
55672     /**
55673      * Sets the tooltip for a column.
55674      * @param {Number} col The column index
55675      * @param {String} tooltip The new tooltip
55676      */
55677     setColumnTooltip : function(col, tooltip){
55678             this.config[col].tooltip = tooltip;
55679     },
55680
55681     /**
55682      * Returns the dataIndex for the specified column.
55683      * @param {Number} col The column index
55684      * @return {Number}
55685      */
55686     getDataIndex : function(col){
55687         return this.config[col].dataIndex;
55688     },
55689
55690     /**
55691      * Sets the dataIndex for a column.
55692      * @param {Number} col The column index
55693      * @param {Number} dataIndex The new dataIndex
55694      */
55695     setDataIndex : function(col, dataIndex){
55696         this.config[col].dataIndex = dataIndex;
55697     },
55698
55699     
55700     
55701     /**
55702      * Returns true if the cell is editable.
55703      * @param {Number} colIndex The column index
55704      * @param {Number} rowIndex The row index
55705      * @return {Boolean}
55706      */
55707     isCellEditable : function(colIndex, rowIndex){
55708         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55709     },
55710
55711     /**
55712      * Returns the editor defined for the cell/column.
55713      * return false or null to disable editing.
55714      * @param {Number} colIndex The column index
55715      * @param {Number} rowIndex The row index
55716      * @return {Object}
55717      */
55718     getCellEditor : function(colIndex, rowIndex){
55719         return this.config[colIndex].editor;
55720     },
55721
55722     /**
55723      * Sets if a column is editable.
55724      * @param {Number} col The column index
55725      * @param {Boolean} editable True if the column is editable
55726      */
55727     setEditable : function(col, editable){
55728         this.config[col].editable = editable;
55729     },
55730
55731
55732     /**
55733      * Returns true if the column is hidden.
55734      * @param {Number} colIndex The column index
55735      * @return {Boolean}
55736      */
55737     isHidden : function(colIndex){
55738         return this.config[colIndex].hidden;
55739     },
55740
55741
55742     /**
55743      * Returns true if the column width cannot be changed
55744      */
55745     isFixed : function(colIndex){
55746         return this.config[colIndex].fixed;
55747     },
55748
55749     /**
55750      * Returns true if the column can be resized
55751      * @return {Boolean}
55752      */
55753     isResizable : function(colIndex){
55754         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55755     },
55756     /**
55757      * Sets if a column is hidden.
55758      * @param {Number} colIndex The column index
55759      * @param {Boolean} hidden True if the column is hidden
55760      */
55761     setHidden : function(colIndex, hidden){
55762         this.config[colIndex].hidden = hidden;
55763         this.totalWidth = null;
55764         this.fireEvent("hiddenchange", this, colIndex, hidden);
55765     },
55766
55767     /**
55768      * Sets the editor for a column.
55769      * @param {Number} col The column index
55770      * @param {Object} editor The editor object
55771      */
55772     setEditor : function(col, editor){
55773         this.config[col].editor = editor;
55774     }
55775 });
55776
55777 Roo.grid.ColumnModel.defaultRenderer = function(value){
55778         if(typeof value == "string" && value.length < 1){
55779             return "&#160;";
55780         }
55781         return value;
55782 };
55783
55784 // Alias for backwards compatibility
55785 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55786 /*
55787  * Based on:
55788  * Ext JS Library 1.1.1
55789  * Copyright(c) 2006-2007, Ext JS, LLC.
55790  *
55791  * Originally Released Under LGPL - original licence link has changed is not relivant.
55792  *
55793  * Fork - LGPL
55794  * <script type="text/javascript">
55795  */
55796
55797 /**
55798  * @class Roo.grid.AbstractSelectionModel
55799  * @extends Roo.util.Observable
55800  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55801  * implemented by descendant classes.  This class should not be directly instantiated.
55802  * @constructor
55803  */
55804 Roo.grid.AbstractSelectionModel = function(){
55805     this.locked = false;
55806     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55807 };
55808
55809 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55810     /** @ignore Called by the grid automatically. Do not call directly. */
55811     init : function(grid){
55812         this.grid = grid;
55813         this.initEvents();
55814     },
55815
55816     /**
55817      * Locks the selections.
55818      */
55819     lock : function(){
55820         this.locked = true;
55821     },
55822
55823     /**
55824      * Unlocks the selections.
55825      */
55826     unlock : function(){
55827         this.locked = false;
55828     },
55829
55830     /**
55831      * Returns true if the selections are locked.
55832      * @return {Boolean}
55833      */
55834     isLocked : function(){
55835         return this.locked;
55836     }
55837 });/*
55838  * Based on:
55839  * Ext JS Library 1.1.1
55840  * Copyright(c) 2006-2007, Ext JS, LLC.
55841  *
55842  * Originally Released Under LGPL - original licence link has changed is not relivant.
55843  *
55844  * Fork - LGPL
55845  * <script type="text/javascript">
55846  */
55847 /**
55848  * @extends Roo.grid.AbstractSelectionModel
55849  * @class Roo.grid.RowSelectionModel
55850  * The default SelectionModel used by {@link Roo.grid.Grid}.
55851  * It supports multiple selections and keyboard selection/navigation. 
55852  * @constructor
55853  * @param {Object} config
55854  */
55855 Roo.grid.RowSelectionModel = function(config){
55856     Roo.apply(this, config);
55857     this.selections = new Roo.util.MixedCollection(false, function(o){
55858         return o.id;
55859     });
55860
55861     this.last = false;
55862     this.lastActive = false;
55863
55864     this.addEvents({
55865         /**
55866              * @event selectionchange
55867              * Fires when the selection changes
55868              * @param {SelectionModel} this
55869              */
55870             "selectionchange" : true,
55871         /**
55872              * @event afterselectionchange
55873              * Fires after the selection changes (eg. by key press or clicking)
55874              * @param {SelectionModel} this
55875              */
55876             "afterselectionchange" : true,
55877         /**
55878              * @event beforerowselect
55879              * Fires when a row is selected being selected, return false to cancel.
55880              * @param {SelectionModel} this
55881              * @param {Number} rowIndex The selected index
55882              * @param {Boolean} keepExisting False if other selections will be cleared
55883              */
55884             "beforerowselect" : true,
55885         /**
55886              * @event rowselect
55887              * Fires when a row is selected.
55888              * @param {SelectionModel} this
55889              * @param {Number} rowIndex The selected index
55890              * @param {Roo.data.Record} r The record
55891              */
55892             "rowselect" : true,
55893         /**
55894              * @event rowdeselect
55895              * Fires when a row is deselected.
55896              * @param {SelectionModel} this
55897              * @param {Number} rowIndex The selected index
55898              */
55899         "rowdeselect" : true
55900     });
55901     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55902     this.locked = false;
55903 };
55904
55905 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55906     /**
55907      * @cfg {Boolean} singleSelect
55908      * True to allow selection of only one row at a time (defaults to false)
55909      */
55910     singleSelect : false,
55911
55912     // private
55913     initEvents : function(){
55914
55915         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55916             this.grid.on("mousedown", this.handleMouseDown, this);
55917         }else{ // allow click to work like normal
55918             this.grid.on("rowclick", this.handleDragableRowClick, this);
55919         }
55920
55921         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55922             "up" : function(e){
55923                 if(!e.shiftKey){
55924                     this.selectPrevious(e.shiftKey);
55925                 }else if(this.last !== false && this.lastActive !== false){
55926                     var last = this.last;
55927                     this.selectRange(this.last,  this.lastActive-1);
55928                     this.grid.getView().focusRow(this.lastActive);
55929                     if(last !== false){
55930                         this.last = last;
55931                     }
55932                 }else{
55933                     this.selectFirstRow();
55934                 }
55935                 this.fireEvent("afterselectionchange", this);
55936             },
55937             "down" : function(e){
55938                 if(!e.shiftKey){
55939                     this.selectNext(e.shiftKey);
55940                 }else if(this.last !== false && this.lastActive !== false){
55941                     var last = this.last;
55942                     this.selectRange(this.last,  this.lastActive+1);
55943                     this.grid.getView().focusRow(this.lastActive);
55944                     if(last !== false){
55945                         this.last = last;
55946                     }
55947                 }else{
55948                     this.selectFirstRow();
55949                 }
55950                 this.fireEvent("afterselectionchange", this);
55951             },
55952             scope: this
55953         });
55954
55955         var view = this.grid.view;
55956         view.on("refresh", this.onRefresh, this);
55957         view.on("rowupdated", this.onRowUpdated, this);
55958         view.on("rowremoved", this.onRemove, this);
55959     },
55960
55961     // private
55962     onRefresh : function(){
55963         var ds = this.grid.dataSource, i, v = this.grid.view;
55964         var s = this.selections;
55965         s.each(function(r){
55966             if((i = ds.indexOfId(r.id)) != -1){
55967                 v.onRowSelect(i);
55968                 s.add(ds.getAt(i)); // updating the selection relate data
55969             }else{
55970                 s.remove(r);
55971             }
55972         });
55973     },
55974
55975     // private
55976     onRemove : function(v, index, r){
55977         this.selections.remove(r);
55978     },
55979
55980     // private
55981     onRowUpdated : function(v, index, r){
55982         if(this.isSelected(r)){
55983             v.onRowSelect(index);
55984         }
55985     },
55986
55987     /**
55988      * Select records.
55989      * @param {Array} records The records to select
55990      * @param {Boolean} keepExisting (optional) True to keep existing selections
55991      */
55992     selectRecords : function(records, keepExisting){
55993         if(!keepExisting){
55994             this.clearSelections();
55995         }
55996         var ds = this.grid.dataSource;
55997         for(var i = 0, len = records.length; i < len; i++){
55998             this.selectRow(ds.indexOf(records[i]), true);
55999         }
56000     },
56001
56002     /**
56003      * Gets the number of selected rows.
56004      * @return {Number}
56005      */
56006     getCount : function(){
56007         return this.selections.length;
56008     },
56009
56010     /**
56011      * Selects the first row in the grid.
56012      */
56013     selectFirstRow : function(){
56014         this.selectRow(0);
56015     },
56016
56017     /**
56018      * Select the last row.
56019      * @param {Boolean} keepExisting (optional) True to keep existing selections
56020      */
56021     selectLastRow : function(keepExisting){
56022         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
56023     },
56024
56025     /**
56026      * Selects the row immediately following the last selected row.
56027      * @param {Boolean} keepExisting (optional) True to keep existing selections
56028      */
56029     selectNext : function(keepExisting){
56030         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
56031             this.selectRow(this.last+1, keepExisting);
56032             this.grid.getView().focusRow(this.last);
56033         }
56034     },
56035
56036     /**
56037      * Selects the row that precedes the last selected row.
56038      * @param {Boolean} keepExisting (optional) True to keep existing selections
56039      */
56040     selectPrevious : function(keepExisting){
56041         if(this.last){
56042             this.selectRow(this.last-1, keepExisting);
56043             this.grid.getView().focusRow(this.last);
56044         }
56045     },
56046
56047     /**
56048      * Returns the selected records
56049      * @return {Array} Array of selected records
56050      */
56051     getSelections : function(){
56052         return [].concat(this.selections.items);
56053     },
56054
56055     /**
56056      * Returns the first selected record.
56057      * @return {Record}
56058      */
56059     getSelected : function(){
56060         return this.selections.itemAt(0);
56061     },
56062
56063
56064     /**
56065      * Clears all selections.
56066      */
56067     clearSelections : function(fast){
56068         if(this.locked) return;
56069         if(fast !== true){
56070             var ds = this.grid.dataSource;
56071             var s = this.selections;
56072             s.each(function(r){
56073                 this.deselectRow(ds.indexOfId(r.id));
56074             }, this);
56075             s.clear();
56076         }else{
56077             this.selections.clear();
56078         }
56079         this.last = false;
56080     },
56081
56082
56083     /**
56084      * Selects all rows.
56085      */
56086     selectAll : function(){
56087         if(this.locked) return;
56088         this.selections.clear();
56089         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56090             this.selectRow(i, true);
56091         }
56092     },
56093
56094     /**
56095      * Returns True if there is a selection.
56096      * @return {Boolean}
56097      */
56098     hasSelection : function(){
56099         return this.selections.length > 0;
56100     },
56101
56102     /**
56103      * Returns True if the specified row is selected.
56104      * @param {Number/Record} record The record or index of the record to check
56105      * @return {Boolean}
56106      */
56107     isSelected : function(index){
56108         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56109         return (r && this.selections.key(r.id) ? true : false);
56110     },
56111
56112     /**
56113      * Returns True if the specified record id is selected.
56114      * @param {String} id The id of record to check
56115      * @return {Boolean}
56116      */
56117     isIdSelected : function(id){
56118         return (this.selections.key(id) ? true : false);
56119     },
56120
56121     // private
56122     handleMouseDown : function(e, t){
56123         var view = this.grid.getView(), rowIndex;
56124         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56125             return;
56126         };
56127         if(e.shiftKey && this.last !== false){
56128             var last = this.last;
56129             this.selectRange(last, rowIndex, e.ctrlKey);
56130             this.last = last; // reset the last
56131             view.focusRow(rowIndex);
56132         }else{
56133             var isSelected = this.isSelected(rowIndex);
56134             if(e.button !== 0 && isSelected){
56135                 view.focusRow(rowIndex);
56136             }else if(e.ctrlKey && isSelected){
56137                 this.deselectRow(rowIndex);
56138             }else if(!isSelected){
56139                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56140                 view.focusRow(rowIndex);
56141             }
56142         }
56143         this.fireEvent("afterselectionchange", this);
56144     },
56145     // private
56146     handleDragableRowClick :  function(grid, rowIndex, e) 
56147     {
56148         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56149             this.selectRow(rowIndex, false);
56150             grid.view.focusRow(rowIndex);
56151              this.fireEvent("afterselectionchange", this);
56152         }
56153     },
56154     
56155     /**
56156      * Selects multiple rows.
56157      * @param {Array} rows Array of the indexes of the row to select
56158      * @param {Boolean} keepExisting (optional) True to keep existing selections
56159      */
56160     selectRows : function(rows, keepExisting){
56161         if(!keepExisting){
56162             this.clearSelections();
56163         }
56164         for(var i = 0, len = rows.length; i < len; i++){
56165             this.selectRow(rows[i], true);
56166         }
56167     },
56168
56169     /**
56170      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56171      * @param {Number} startRow The index of the first row in the range
56172      * @param {Number} endRow The index of the last row in the range
56173      * @param {Boolean} keepExisting (optional) True to retain existing selections
56174      */
56175     selectRange : function(startRow, endRow, keepExisting){
56176         if(this.locked) return;
56177         if(!keepExisting){
56178             this.clearSelections();
56179         }
56180         if(startRow <= endRow){
56181             for(var i = startRow; i <= endRow; i++){
56182                 this.selectRow(i, true);
56183             }
56184         }else{
56185             for(var i = startRow; i >= endRow; i--){
56186                 this.selectRow(i, true);
56187             }
56188         }
56189     },
56190
56191     /**
56192      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56193      * @param {Number} startRow The index of the first row in the range
56194      * @param {Number} endRow The index of the last row in the range
56195      */
56196     deselectRange : function(startRow, endRow, preventViewNotify){
56197         if(this.locked) return;
56198         for(var i = startRow; i <= endRow; i++){
56199             this.deselectRow(i, preventViewNotify);
56200         }
56201     },
56202
56203     /**
56204      * Selects a row.
56205      * @param {Number} row The index of the row to select
56206      * @param {Boolean} keepExisting (optional) True to keep existing selections
56207      */
56208     selectRow : function(index, keepExisting, preventViewNotify){
56209         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56210         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56211             if(!keepExisting || this.singleSelect){
56212                 this.clearSelections();
56213             }
56214             var r = this.grid.dataSource.getAt(index);
56215             this.selections.add(r);
56216             this.last = this.lastActive = index;
56217             if(!preventViewNotify){
56218                 this.grid.getView().onRowSelect(index);
56219             }
56220             this.fireEvent("rowselect", this, index, r);
56221             this.fireEvent("selectionchange", this);
56222         }
56223     },
56224
56225     /**
56226      * Deselects a row.
56227      * @param {Number} row The index of the row to deselect
56228      */
56229     deselectRow : function(index, preventViewNotify){
56230         if(this.locked) return;
56231         if(this.last == index){
56232             this.last = false;
56233         }
56234         if(this.lastActive == index){
56235             this.lastActive = false;
56236         }
56237         var r = this.grid.dataSource.getAt(index);
56238         this.selections.remove(r);
56239         if(!preventViewNotify){
56240             this.grid.getView().onRowDeselect(index);
56241         }
56242         this.fireEvent("rowdeselect", this, index);
56243         this.fireEvent("selectionchange", this);
56244     },
56245
56246     // private
56247     restoreLast : function(){
56248         if(this._last){
56249             this.last = this._last;
56250         }
56251     },
56252
56253     // private
56254     acceptsNav : function(row, col, cm){
56255         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56256     },
56257
56258     // private
56259     onEditorKey : function(field, e){
56260         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56261         if(k == e.TAB){
56262             e.stopEvent();
56263             ed.completeEdit();
56264             if(e.shiftKey){
56265                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56266             }else{
56267                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56268             }
56269         }else if(k == e.ENTER && !e.ctrlKey){
56270             e.stopEvent();
56271             ed.completeEdit();
56272             if(e.shiftKey){
56273                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56274             }else{
56275                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56276             }
56277         }else if(k == e.ESC){
56278             ed.cancelEdit();
56279         }
56280         if(newCell){
56281             g.startEditing(newCell[0], newCell[1]);
56282         }
56283     }
56284 });/*
56285  * Based on:
56286  * Ext JS Library 1.1.1
56287  * Copyright(c) 2006-2007, Ext JS, LLC.
56288  *
56289  * Originally Released Under LGPL - original licence link has changed is not relivant.
56290  *
56291  * Fork - LGPL
56292  * <script type="text/javascript">
56293  */
56294 /**
56295  * @class Roo.grid.CellSelectionModel
56296  * @extends Roo.grid.AbstractSelectionModel
56297  * This class provides the basic implementation for cell selection in a grid.
56298  * @constructor
56299  * @param {Object} config The object containing the configuration of this model.
56300  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56301  */
56302 Roo.grid.CellSelectionModel = function(config){
56303     Roo.apply(this, config);
56304
56305     this.selection = null;
56306
56307     this.addEvents({
56308         /**
56309              * @event beforerowselect
56310              * Fires before a cell is selected.
56311              * @param {SelectionModel} this
56312              * @param {Number} rowIndex The selected row index
56313              * @param {Number} colIndex The selected cell index
56314              */
56315             "beforecellselect" : true,
56316         /**
56317              * @event cellselect
56318              * Fires when a cell is selected.
56319              * @param {SelectionModel} this
56320              * @param {Number} rowIndex The selected row index
56321              * @param {Number} colIndex The selected cell index
56322              */
56323             "cellselect" : true,
56324         /**
56325              * @event selectionchange
56326              * Fires when the active selection changes.
56327              * @param {SelectionModel} this
56328              * @param {Object} selection null for no selection or an object (o) with two properties
56329                 <ul>
56330                 <li>o.record: the record object for the row the selection is in</li>
56331                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56332                 </ul>
56333              */
56334             "selectionchange" : true,
56335         /**
56336              * @event tabend
56337              * Fires when the tab (or enter) was pressed on the last editable cell
56338              * You can use this to trigger add new row.
56339              * @param {SelectionModel} this
56340              */
56341             "tabend" : true,
56342          /**
56343              * @event beforeeditnext
56344              * Fires before the next editable sell is made active
56345              * You can use this to skip to another cell or fire the tabend
56346              *    if you set cell to false
56347              * @param {Object} eventdata object : { cell : [ row, col ] } 
56348              */
56349             "beforeeditnext" : true
56350     });
56351     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56352 };
56353
56354 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56355     
56356     enter_is_tab: false,
56357
56358     /** @ignore */
56359     initEvents : function(){
56360         this.grid.on("mousedown", this.handleMouseDown, this);
56361         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56362         var view = this.grid.view;
56363         view.on("refresh", this.onViewChange, this);
56364         view.on("rowupdated", this.onRowUpdated, this);
56365         view.on("beforerowremoved", this.clearSelections, this);
56366         view.on("beforerowsinserted", this.clearSelections, this);
56367         if(this.grid.isEditor){
56368             this.grid.on("beforeedit", this.beforeEdit,  this);
56369         }
56370     },
56371
56372         //private
56373     beforeEdit : function(e){
56374         this.select(e.row, e.column, false, true, e.record);
56375     },
56376
56377         //private
56378     onRowUpdated : function(v, index, r){
56379         if(this.selection && this.selection.record == r){
56380             v.onCellSelect(index, this.selection.cell[1]);
56381         }
56382     },
56383
56384         //private
56385     onViewChange : function(){
56386         this.clearSelections(true);
56387     },
56388
56389         /**
56390          * Returns the currently selected cell,.
56391          * @return {Array} The selected cell (row, column) or null if none selected.
56392          */
56393     getSelectedCell : function(){
56394         return this.selection ? this.selection.cell : null;
56395     },
56396
56397     /**
56398      * Clears all selections.
56399      * @param {Boolean} true to prevent the gridview from being notified about the change.
56400      */
56401     clearSelections : function(preventNotify){
56402         var s = this.selection;
56403         if(s){
56404             if(preventNotify !== true){
56405                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56406             }
56407             this.selection = null;
56408             this.fireEvent("selectionchange", this, null);
56409         }
56410     },
56411
56412     /**
56413      * Returns true if there is a selection.
56414      * @return {Boolean}
56415      */
56416     hasSelection : function(){
56417         return this.selection ? true : false;
56418     },
56419
56420     /** @ignore */
56421     handleMouseDown : function(e, t){
56422         var v = this.grid.getView();
56423         if(this.isLocked()){
56424             return;
56425         };
56426         var row = v.findRowIndex(t);
56427         var cell = v.findCellIndex(t);
56428         if(row !== false && cell !== false){
56429             this.select(row, cell);
56430         }
56431     },
56432
56433     /**
56434      * Selects a cell.
56435      * @param {Number} rowIndex
56436      * @param {Number} collIndex
56437      */
56438     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56439         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56440             this.clearSelections();
56441             r = r || this.grid.dataSource.getAt(rowIndex);
56442             this.selection = {
56443                 record : r,
56444                 cell : [rowIndex, colIndex]
56445             };
56446             if(!preventViewNotify){
56447                 var v = this.grid.getView();
56448                 v.onCellSelect(rowIndex, colIndex);
56449                 if(preventFocus !== true){
56450                     v.focusCell(rowIndex, colIndex);
56451                 }
56452             }
56453             this.fireEvent("cellselect", this, rowIndex, colIndex);
56454             this.fireEvent("selectionchange", this, this.selection);
56455         }
56456     },
56457
56458         //private
56459     isSelectable : function(rowIndex, colIndex, cm){
56460         return !cm.isHidden(colIndex);
56461     },
56462
56463     /** @ignore */
56464     handleKeyDown : function(e){
56465         //Roo.log('Cell Sel Model handleKeyDown');
56466         if(!e.isNavKeyPress()){
56467             return;
56468         }
56469         var g = this.grid, s = this.selection;
56470         if(!s){
56471             e.stopEvent();
56472             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56473             if(cell){
56474                 this.select(cell[0], cell[1]);
56475             }
56476             return;
56477         }
56478         var sm = this;
56479         var walk = function(row, col, step){
56480             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56481         };
56482         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56483         var newCell;
56484
56485       
56486
56487         switch(k){
56488             case e.TAB:
56489                 // handled by onEditorKey
56490                 if (g.isEditor && g.editing) {
56491                     return;
56492                 }
56493                 if(e.shiftKey) {
56494                     newCell = walk(r, c-1, -1);
56495                 } else {
56496                     newCell = walk(r, c+1, 1);
56497                 }
56498                 break;
56499             
56500             case e.DOWN:
56501                newCell = walk(r+1, c, 1);
56502                 break;
56503             
56504             case e.UP:
56505                 newCell = walk(r-1, c, -1);
56506                 break;
56507             
56508             case e.RIGHT:
56509                 newCell = walk(r, c+1, 1);
56510                 break;
56511             
56512             case e.LEFT:
56513                 newCell = walk(r, c-1, -1);
56514                 break;
56515             
56516             case e.ENTER:
56517                 
56518                 if(g.isEditor && !g.editing){
56519                    g.startEditing(r, c);
56520                    e.stopEvent();
56521                    return;
56522                 }
56523                 
56524                 
56525              break;
56526         };
56527         if(newCell){
56528             this.select(newCell[0], newCell[1]);
56529             e.stopEvent();
56530             
56531         }
56532     },
56533
56534     acceptsNav : function(row, col, cm){
56535         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56536     },
56537     /**
56538      * Selects a cell.
56539      * @param {Number} field (not used) - as it's normally used as a listener
56540      * @param {Number} e - event - fake it by using
56541      *
56542      * var e = Roo.EventObjectImpl.prototype;
56543      * e.keyCode = e.TAB
56544      *
56545      * 
56546      */
56547     onEditorKey : function(field, e){
56548         
56549         var k = e.getKey(),
56550             newCell,
56551             g = this.grid,
56552             ed = g.activeEditor,
56553             forward = false;
56554         ///Roo.log('onEditorKey' + k);
56555         
56556         
56557         if (this.enter_is_tab && k == e.ENTER) {
56558             k = e.TAB;
56559         }
56560         
56561         if(k == e.TAB){
56562             if(e.shiftKey){
56563                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56564             }else{
56565                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56566                 forward = true;
56567             }
56568             
56569             e.stopEvent();
56570             
56571         } else if(k == e.ENTER &&  !e.ctrlKey){
56572             ed.completeEdit();
56573             e.stopEvent();
56574             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56575         
56576                 } else if(k == e.ESC){
56577             ed.cancelEdit();
56578         }
56579                 
56580         if (newCell) {
56581             var ecall = { cell : newCell, forward : forward };
56582             this.fireEvent('beforeeditnext', ecall );
56583             newCell = ecall.cell;
56584                         forward = ecall.forward;
56585         }
56586                 
56587         if(newCell){
56588             //Roo.log('next cell after edit');
56589             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56590         } else if (forward) {
56591             // tabbed past last
56592             this.fireEvent.defer(100, this, ['tabend',this]);
56593         }
56594     }
56595 });/*
56596  * Based on:
56597  * Ext JS Library 1.1.1
56598  * Copyright(c) 2006-2007, Ext JS, LLC.
56599  *
56600  * Originally Released Under LGPL - original licence link has changed is not relivant.
56601  *
56602  * Fork - LGPL
56603  * <script type="text/javascript">
56604  */
56605  
56606 /**
56607  * @class Roo.grid.EditorGrid
56608  * @extends Roo.grid.Grid
56609  * Class for creating and editable grid.
56610  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56611  * The container MUST have some type of size defined for the grid to fill. The container will be 
56612  * automatically set to position relative if it isn't already.
56613  * @param {Object} dataSource The data model to bind to
56614  * @param {Object} colModel The column model with info about this grid's columns
56615  */
56616 Roo.grid.EditorGrid = function(container, config){
56617     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56618     this.getGridEl().addClass("xedit-grid");
56619
56620     if(!this.selModel){
56621         this.selModel = new Roo.grid.CellSelectionModel();
56622     }
56623
56624     this.activeEditor = null;
56625
56626         this.addEvents({
56627             /**
56628              * @event beforeedit
56629              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56630              * <ul style="padding:5px;padding-left:16px;">
56631              * <li>grid - This grid</li>
56632              * <li>record - The record being edited</li>
56633              * <li>field - The field name being edited</li>
56634              * <li>value - The value for the field being edited.</li>
56635              * <li>row - The grid row index</li>
56636              * <li>column - The grid column index</li>
56637              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56638              * </ul>
56639              * @param {Object} e An edit event (see above for description)
56640              */
56641             "beforeedit" : true,
56642             /**
56643              * @event afteredit
56644              * Fires after a cell is edited. <br />
56645              * <ul style="padding:5px;padding-left:16px;">
56646              * <li>grid - This grid</li>
56647              * <li>record - The record being edited</li>
56648              * <li>field - The field name being edited</li>
56649              * <li>value - The value being set</li>
56650              * <li>originalValue - The original value for the field, before the edit.</li>
56651              * <li>row - The grid row index</li>
56652              * <li>column - The grid column index</li>
56653              * </ul>
56654              * @param {Object} e An edit event (see above for description)
56655              */
56656             "afteredit" : true,
56657             /**
56658              * @event validateedit
56659              * Fires after a cell is edited, but before the value is set in the record. 
56660          * You can use this to modify the value being set in the field, Return false
56661              * to cancel the change. The edit event object has the following properties <br />
56662              * <ul style="padding:5px;padding-left:16px;">
56663          * <li>editor - This editor</li>
56664              * <li>grid - This grid</li>
56665              * <li>record - The record being edited</li>
56666              * <li>field - The field name being edited</li>
56667              * <li>value - The value being set</li>
56668              * <li>originalValue - The original value for the field, before the edit.</li>
56669              * <li>row - The grid row index</li>
56670              * <li>column - The grid column index</li>
56671              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56672              * </ul>
56673              * @param {Object} e An edit event (see above for description)
56674              */
56675             "validateedit" : true
56676         });
56677     this.on("bodyscroll", this.stopEditing,  this);
56678     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56679 };
56680
56681 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56682     /**
56683      * @cfg {Number} clicksToEdit
56684      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56685      */
56686     clicksToEdit: 2,
56687
56688     // private
56689     isEditor : true,
56690     // private
56691     trackMouseOver: false, // causes very odd FF errors
56692
56693     onCellDblClick : function(g, row, col){
56694         this.startEditing(row, col);
56695     },
56696
56697     onEditComplete : function(ed, value, startValue){
56698         this.editing = false;
56699         this.activeEditor = null;
56700         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56701         var r = ed.record;
56702         var field = this.colModel.getDataIndex(ed.col);
56703         var e = {
56704             grid: this,
56705             record: r,
56706             field: field,
56707             originalValue: startValue,
56708             value: value,
56709             row: ed.row,
56710             column: ed.col,
56711             cancel:false,
56712             editor: ed
56713         };
56714         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56715         cell.show();
56716           
56717         if(String(value) !== String(startValue)){
56718             
56719             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56720                 r.set(field, e.value);
56721                 // if we are dealing with a combo box..
56722                 // then we also set the 'name' colum to be the displayField
56723                 if (ed.field.displayField && ed.field.name) {
56724                     r.set(ed.field.name, ed.field.el.dom.value);
56725                 }
56726                 
56727                 delete e.cancel; //?? why!!!
56728                 this.fireEvent("afteredit", e);
56729             }
56730         } else {
56731             this.fireEvent("afteredit", e); // always fire it!
56732         }
56733         this.view.focusCell(ed.row, ed.col);
56734     },
56735
56736     /**
56737      * Starts editing the specified for the specified row/column
56738      * @param {Number} rowIndex
56739      * @param {Number} colIndex
56740      */
56741     startEditing : function(row, col){
56742         this.stopEditing();
56743         if(this.colModel.isCellEditable(col, row)){
56744             this.view.ensureVisible(row, col, true);
56745           
56746             var r = this.dataSource.getAt(row);
56747             var field = this.colModel.getDataIndex(col);
56748             var cell = Roo.get(this.view.getCell(row,col));
56749             var e = {
56750                 grid: this,
56751                 record: r,
56752                 field: field,
56753                 value: r.data[field],
56754                 row: row,
56755                 column: col,
56756                 cancel:false 
56757             };
56758             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56759                 this.editing = true;
56760                 var ed = this.colModel.getCellEditor(col, row);
56761                 
56762                 if (!ed) {
56763                     return;
56764                 }
56765                 if(!ed.rendered){
56766                     ed.render(ed.parentEl || document.body);
56767                 }
56768                 ed.field.reset();
56769                
56770                 cell.hide();
56771                 
56772                 (function(){ // complex but required for focus issues in safari, ie and opera
56773                     ed.row = row;
56774                     ed.col = col;
56775                     ed.record = r;
56776                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56777                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56778                     this.activeEditor = ed;
56779                     var v = r.data[field];
56780                     ed.startEdit(this.view.getCell(row, col), v);
56781                     // combo's with 'displayField and name set
56782                     if (ed.field.displayField && ed.field.name) {
56783                         ed.field.el.dom.value = r.data[ed.field.name];
56784                     }
56785                     
56786                     
56787                 }).defer(50, this);
56788             }
56789         }
56790     },
56791         
56792     /**
56793      * Stops any active editing
56794      */
56795     stopEditing : function(){
56796         if(this.activeEditor){
56797             this.activeEditor.completeEdit();
56798         }
56799         this.activeEditor = null;
56800     },
56801         
56802          /**
56803      * Called to get grid's drag proxy text, by default returns this.ddText.
56804      * @return {String}
56805      */
56806     getDragDropText : function(){
56807         var count = this.selModel.getSelectedCell() ? 1 : 0;
56808         return String.format(this.ddText, count, count == 1 ? '' : 's');
56809     }
56810         
56811 });/*
56812  * Based on:
56813  * Ext JS Library 1.1.1
56814  * Copyright(c) 2006-2007, Ext JS, LLC.
56815  *
56816  * Originally Released Under LGPL - original licence link has changed is not relivant.
56817  *
56818  * Fork - LGPL
56819  * <script type="text/javascript">
56820  */
56821
56822 // private - not really -- you end up using it !
56823 // This is a support class used internally by the Grid components
56824
56825 /**
56826  * @class Roo.grid.GridEditor
56827  * @extends Roo.Editor
56828  * Class for creating and editable grid elements.
56829  * @param {Object} config any settings (must include field)
56830  */
56831 Roo.grid.GridEditor = function(field, config){
56832     if (!config && field.field) {
56833         config = field;
56834         field = Roo.factory(config.field, Roo.form);
56835     }
56836     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56837     field.monitorTab = false;
56838 };
56839
56840 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56841     
56842     /**
56843      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56844      */
56845     
56846     alignment: "tl-tl",
56847     autoSize: "width",
56848     hideEl : false,
56849     cls: "x-small-editor x-grid-editor",
56850     shim:false,
56851     shadow:"frame"
56852 });/*
56853  * Based on:
56854  * Ext JS Library 1.1.1
56855  * Copyright(c) 2006-2007, Ext JS, LLC.
56856  *
56857  * Originally Released Under LGPL - original licence link has changed is not relivant.
56858  *
56859  * Fork - LGPL
56860  * <script type="text/javascript">
56861  */
56862   
56863
56864   
56865 Roo.grid.PropertyRecord = Roo.data.Record.create([
56866     {name:'name',type:'string'},  'value'
56867 ]);
56868
56869
56870 Roo.grid.PropertyStore = function(grid, source){
56871     this.grid = grid;
56872     this.store = new Roo.data.Store({
56873         recordType : Roo.grid.PropertyRecord
56874     });
56875     this.store.on('update', this.onUpdate,  this);
56876     if(source){
56877         this.setSource(source);
56878     }
56879     Roo.grid.PropertyStore.superclass.constructor.call(this);
56880 };
56881
56882
56883
56884 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56885     setSource : function(o){
56886         this.source = o;
56887         this.store.removeAll();
56888         var data = [];
56889         for(var k in o){
56890             if(this.isEditableValue(o[k])){
56891                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56892             }
56893         }
56894         this.store.loadRecords({records: data}, {}, true);
56895     },
56896
56897     onUpdate : function(ds, record, type){
56898         if(type == Roo.data.Record.EDIT){
56899             var v = record.data['value'];
56900             var oldValue = record.modified['value'];
56901             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56902                 this.source[record.id] = v;
56903                 record.commit();
56904                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56905             }else{
56906                 record.reject();
56907             }
56908         }
56909     },
56910
56911     getProperty : function(row){
56912        return this.store.getAt(row);
56913     },
56914
56915     isEditableValue: function(val){
56916         if(val && val instanceof Date){
56917             return true;
56918         }else if(typeof val == 'object' || typeof val == 'function'){
56919             return false;
56920         }
56921         return true;
56922     },
56923
56924     setValue : function(prop, value){
56925         this.source[prop] = value;
56926         this.store.getById(prop).set('value', value);
56927     },
56928
56929     getSource : function(){
56930         return this.source;
56931     }
56932 });
56933
56934 Roo.grid.PropertyColumnModel = function(grid, store){
56935     this.grid = grid;
56936     var g = Roo.grid;
56937     g.PropertyColumnModel.superclass.constructor.call(this, [
56938         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56939         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56940     ]);
56941     this.store = store;
56942     this.bselect = Roo.DomHelper.append(document.body, {
56943         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56944             {tag: 'option', value: 'true', html: 'true'},
56945             {tag: 'option', value: 'false', html: 'false'}
56946         ]
56947     });
56948     Roo.id(this.bselect);
56949     var f = Roo.form;
56950     this.editors = {
56951         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56952         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56953         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56954         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56955         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56956     };
56957     this.renderCellDelegate = this.renderCell.createDelegate(this);
56958     this.renderPropDelegate = this.renderProp.createDelegate(this);
56959 };
56960
56961 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56962     
56963     
56964     nameText : 'Name',
56965     valueText : 'Value',
56966     
56967     dateFormat : 'm/j/Y',
56968     
56969     
56970     renderDate : function(dateVal){
56971         return dateVal.dateFormat(this.dateFormat);
56972     },
56973
56974     renderBool : function(bVal){
56975         return bVal ? 'true' : 'false';
56976     },
56977
56978     isCellEditable : function(colIndex, rowIndex){
56979         return colIndex == 1;
56980     },
56981
56982     getRenderer : function(col){
56983         return col == 1 ?
56984             this.renderCellDelegate : this.renderPropDelegate;
56985     },
56986
56987     renderProp : function(v){
56988         return this.getPropertyName(v);
56989     },
56990
56991     renderCell : function(val){
56992         var rv = val;
56993         if(val instanceof Date){
56994             rv = this.renderDate(val);
56995         }else if(typeof val == 'boolean'){
56996             rv = this.renderBool(val);
56997         }
56998         return Roo.util.Format.htmlEncode(rv);
56999     },
57000
57001     getPropertyName : function(name){
57002         var pn = this.grid.propertyNames;
57003         return pn && pn[name] ? pn[name] : name;
57004     },
57005
57006     getCellEditor : function(colIndex, rowIndex){
57007         var p = this.store.getProperty(rowIndex);
57008         var n = p.data['name'], val = p.data['value'];
57009         
57010         if(typeof(this.grid.customEditors[n]) == 'string'){
57011             return this.editors[this.grid.customEditors[n]];
57012         }
57013         if(typeof(this.grid.customEditors[n]) != 'undefined'){
57014             return this.grid.customEditors[n];
57015         }
57016         if(val instanceof Date){
57017             return this.editors['date'];
57018         }else if(typeof val == 'number'){
57019             return this.editors['number'];
57020         }else if(typeof val == 'boolean'){
57021             return this.editors['boolean'];
57022         }else{
57023             return this.editors['string'];
57024         }
57025     }
57026 });
57027
57028 /**
57029  * @class Roo.grid.PropertyGrid
57030  * @extends Roo.grid.EditorGrid
57031  * This class represents the  interface of a component based property grid control.
57032  * <br><br>Usage:<pre><code>
57033  var grid = new Roo.grid.PropertyGrid("my-container-id", {
57034       
57035  });
57036  // set any options
57037  grid.render();
57038  * </code></pre>
57039   
57040  * @constructor
57041  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57042  * The container MUST have some type of size defined for the grid to fill. The container will be
57043  * automatically set to position relative if it isn't already.
57044  * @param {Object} config A config object that sets properties on this grid.
57045  */
57046 Roo.grid.PropertyGrid = function(container, config){
57047     config = config || {};
57048     var store = new Roo.grid.PropertyStore(this);
57049     this.store = store;
57050     var cm = new Roo.grid.PropertyColumnModel(this, store);
57051     store.store.sort('name', 'ASC');
57052     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57053         ds: store.store,
57054         cm: cm,
57055         enableColLock:false,
57056         enableColumnMove:false,
57057         stripeRows:false,
57058         trackMouseOver: false,
57059         clicksToEdit:1
57060     }, config));
57061     this.getGridEl().addClass('x-props-grid');
57062     this.lastEditRow = null;
57063     this.on('columnresize', this.onColumnResize, this);
57064     this.addEvents({
57065          /**
57066              * @event beforepropertychange
57067              * Fires before a property changes (return false to stop?)
57068              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57069              * @param {String} id Record Id
57070              * @param {String} newval New Value
57071          * @param {String} oldval Old Value
57072              */
57073         "beforepropertychange": true,
57074         /**
57075              * @event propertychange
57076              * Fires after a property changes
57077              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57078              * @param {String} id Record Id
57079              * @param {String} newval New Value
57080          * @param {String} oldval Old Value
57081              */
57082         "propertychange": true
57083     });
57084     this.customEditors = this.customEditors || {};
57085 };
57086 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57087     
57088      /**
57089      * @cfg {Object} customEditors map of colnames=> custom editors.
57090      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57091      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57092      * false disables editing of the field.
57093          */
57094     
57095       /**
57096      * @cfg {Object} propertyNames map of property Names to their displayed value
57097          */
57098     
57099     render : function(){
57100         Roo.grid.PropertyGrid.superclass.render.call(this);
57101         this.autoSize.defer(100, this);
57102     },
57103
57104     autoSize : function(){
57105         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57106         if(this.view){
57107             this.view.fitColumns();
57108         }
57109     },
57110
57111     onColumnResize : function(){
57112         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57113         this.autoSize();
57114     },
57115     /**
57116      * Sets the data for the Grid
57117      * accepts a Key => Value object of all the elements avaiable.
57118      * @param {Object} data  to appear in grid.
57119      */
57120     setSource : function(source){
57121         this.store.setSource(source);
57122         //this.autoSize();
57123     },
57124     /**
57125      * Gets all the data from the grid.
57126      * @return {Object} data  data stored in grid
57127      */
57128     getSource : function(){
57129         return this.store.getSource();
57130     }
57131 });/*
57132   
57133  * Licence LGPL
57134  
57135  */
57136  
57137 /**
57138  * @class Roo.grid.Calendar
57139  * @extends Roo.util.Grid
57140  * This class extends the Grid to provide a calendar widget
57141  * <br><br>Usage:<pre><code>
57142  var grid = new Roo.grid.Calendar("my-container-id", {
57143      ds: myDataStore,
57144      cm: myColModel,
57145      selModel: mySelectionModel,
57146      autoSizeColumns: true,
57147      monitorWindowResize: false,
57148      trackMouseOver: true
57149      eventstore : real data store..
57150  });
57151  // set any options
57152  grid.render();
57153   
57154   * @constructor
57155  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57156  * The container MUST have some type of size defined for the grid to fill. The container will be
57157  * automatically set to position relative if it isn't already.
57158  * @param {Object} config A config object that sets properties on this grid.
57159  */
57160 Roo.grid.Calendar = function(container, config){
57161         // initialize the container
57162         this.container = Roo.get(container);
57163         this.container.update("");
57164         this.container.setStyle("overflow", "hidden");
57165     this.container.addClass('x-grid-container');
57166
57167     this.id = this.container.id;
57168
57169     Roo.apply(this, config);
57170     // check and correct shorthanded configs
57171     
57172     var rows = [];
57173     var d =1;
57174     for (var r = 0;r < 6;r++) {
57175         
57176         rows[r]=[];
57177         for (var c =0;c < 7;c++) {
57178             rows[r][c]= '';
57179         }
57180     }
57181     if (this.eventStore) {
57182         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57183         this.eventStore.on('load',this.onLoad, this);
57184         this.eventStore.on('beforeload',this.clearEvents, this);
57185          
57186     }
57187     
57188     this.dataSource = new Roo.data.Store({
57189             proxy: new Roo.data.MemoryProxy(rows),
57190             reader: new Roo.data.ArrayReader({}, [
57191                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57192     });
57193
57194     this.dataSource.load();
57195     this.ds = this.dataSource;
57196     this.ds.xmodule = this.xmodule || false;
57197     
57198     
57199     var cellRender = function(v,x,r)
57200     {
57201         return String.format(
57202             '<div class="fc-day  fc-widget-content"><div>' +
57203                 '<div class="fc-event-container"></div>' +
57204                 '<div class="fc-day-number">{0}</div>'+
57205                 
57206                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57207             '</div></div>', v);
57208     
57209     }
57210     
57211     
57212     this.colModel = new Roo.grid.ColumnModel( [
57213         {
57214             xtype: 'ColumnModel',
57215             xns: Roo.grid,
57216             dataIndex : 'weekday0',
57217             header : 'Sunday',
57218             renderer : cellRender
57219         },
57220         {
57221             xtype: 'ColumnModel',
57222             xns: Roo.grid,
57223             dataIndex : 'weekday1',
57224             header : 'Monday',
57225             renderer : cellRender
57226         },
57227         {
57228             xtype: 'ColumnModel',
57229             xns: Roo.grid,
57230             dataIndex : 'weekday2',
57231             header : 'Tuesday',
57232             renderer : cellRender
57233         },
57234         {
57235             xtype: 'ColumnModel',
57236             xns: Roo.grid,
57237             dataIndex : 'weekday3',
57238             header : 'Wednesday',
57239             renderer : cellRender
57240         },
57241         {
57242             xtype: 'ColumnModel',
57243             xns: Roo.grid,
57244             dataIndex : 'weekday4',
57245             header : 'Thursday',
57246             renderer : cellRender
57247         },
57248         {
57249             xtype: 'ColumnModel',
57250             xns: Roo.grid,
57251             dataIndex : 'weekday5',
57252             header : 'Friday',
57253             renderer : cellRender
57254         },
57255         {
57256             xtype: 'ColumnModel',
57257             xns: Roo.grid,
57258             dataIndex : 'weekday6',
57259             header : 'Saturday',
57260             renderer : cellRender
57261         }
57262     ]);
57263     this.cm = this.colModel;
57264     this.cm.xmodule = this.xmodule || false;
57265  
57266         
57267           
57268     //this.selModel = new Roo.grid.CellSelectionModel();
57269     //this.sm = this.selModel;
57270     //this.selModel.init(this);
57271     
57272     
57273     if(this.width){
57274         this.container.setWidth(this.width);
57275     }
57276
57277     if(this.height){
57278         this.container.setHeight(this.height);
57279     }
57280     /** @private */
57281         this.addEvents({
57282         // raw events
57283         /**
57284          * @event click
57285          * The raw click event for the entire grid.
57286          * @param {Roo.EventObject} e
57287          */
57288         "click" : true,
57289         /**
57290          * @event dblclick
57291          * The raw dblclick event for the entire grid.
57292          * @param {Roo.EventObject} e
57293          */
57294         "dblclick" : true,
57295         /**
57296          * @event contextmenu
57297          * The raw contextmenu event for the entire grid.
57298          * @param {Roo.EventObject} e
57299          */
57300         "contextmenu" : true,
57301         /**
57302          * @event mousedown
57303          * The raw mousedown event for the entire grid.
57304          * @param {Roo.EventObject} e
57305          */
57306         "mousedown" : true,
57307         /**
57308          * @event mouseup
57309          * The raw mouseup event for the entire grid.
57310          * @param {Roo.EventObject} e
57311          */
57312         "mouseup" : true,
57313         /**
57314          * @event mouseover
57315          * The raw mouseover event for the entire grid.
57316          * @param {Roo.EventObject} e
57317          */
57318         "mouseover" : true,
57319         /**
57320          * @event mouseout
57321          * The raw mouseout event for the entire grid.
57322          * @param {Roo.EventObject} e
57323          */
57324         "mouseout" : true,
57325         /**
57326          * @event keypress
57327          * The raw keypress event for the entire grid.
57328          * @param {Roo.EventObject} e
57329          */
57330         "keypress" : true,
57331         /**
57332          * @event keydown
57333          * The raw keydown event for the entire grid.
57334          * @param {Roo.EventObject} e
57335          */
57336         "keydown" : true,
57337
57338         // custom events
57339
57340         /**
57341          * @event cellclick
57342          * Fires when a cell is clicked
57343          * @param {Grid} this
57344          * @param {Number} rowIndex
57345          * @param {Number} columnIndex
57346          * @param {Roo.EventObject} e
57347          */
57348         "cellclick" : true,
57349         /**
57350          * @event celldblclick
57351          * Fires when a cell is double clicked
57352          * @param {Grid} this
57353          * @param {Number} rowIndex
57354          * @param {Number} columnIndex
57355          * @param {Roo.EventObject} e
57356          */
57357         "celldblclick" : true,
57358         /**
57359          * @event rowclick
57360          * Fires when a row is clicked
57361          * @param {Grid} this
57362          * @param {Number} rowIndex
57363          * @param {Roo.EventObject} e
57364          */
57365         "rowclick" : true,
57366         /**
57367          * @event rowdblclick
57368          * Fires when a row is double clicked
57369          * @param {Grid} this
57370          * @param {Number} rowIndex
57371          * @param {Roo.EventObject} e
57372          */
57373         "rowdblclick" : true,
57374         /**
57375          * @event headerclick
57376          * Fires when a header is clicked
57377          * @param {Grid} this
57378          * @param {Number} columnIndex
57379          * @param {Roo.EventObject} e
57380          */
57381         "headerclick" : true,
57382         /**
57383          * @event headerdblclick
57384          * Fires when a header cell is double clicked
57385          * @param {Grid} this
57386          * @param {Number} columnIndex
57387          * @param {Roo.EventObject} e
57388          */
57389         "headerdblclick" : true,
57390         /**
57391          * @event rowcontextmenu
57392          * Fires when a row is right clicked
57393          * @param {Grid} this
57394          * @param {Number} rowIndex
57395          * @param {Roo.EventObject} e
57396          */
57397         "rowcontextmenu" : true,
57398         /**
57399          * @event cellcontextmenu
57400          * Fires when a cell is right clicked
57401          * @param {Grid} this
57402          * @param {Number} rowIndex
57403          * @param {Number} cellIndex
57404          * @param {Roo.EventObject} e
57405          */
57406          "cellcontextmenu" : true,
57407         /**
57408          * @event headercontextmenu
57409          * Fires when a header is right clicked
57410          * @param {Grid} this
57411          * @param {Number} columnIndex
57412          * @param {Roo.EventObject} e
57413          */
57414         "headercontextmenu" : true,
57415         /**
57416          * @event bodyscroll
57417          * Fires when the body element is scrolled
57418          * @param {Number} scrollLeft
57419          * @param {Number} scrollTop
57420          */
57421         "bodyscroll" : true,
57422         /**
57423          * @event columnresize
57424          * Fires when the user resizes a column
57425          * @param {Number} columnIndex
57426          * @param {Number} newSize
57427          */
57428         "columnresize" : true,
57429         /**
57430          * @event columnmove
57431          * Fires when the user moves a column
57432          * @param {Number} oldIndex
57433          * @param {Number} newIndex
57434          */
57435         "columnmove" : true,
57436         /**
57437          * @event startdrag
57438          * Fires when row(s) start being dragged
57439          * @param {Grid} this
57440          * @param {Roo.GridDD} dd The drag drop object
57441          * @param {event} e The raw browser event
57442          */
57443         "startdrag" : true,
57444         /**
57445          * @event enddrag
57446          * Fires when a drag operation is complete
57447          * @param {Grid} this
57448          * @param {Roo.GridDD} dd The drag drop object
57449          * @param {event} e The raw browser event
57450          */
57451         "enddrag" : true,
57452         /**
57453          * @event dragdrop
57454          * Fires when dragged row(s) are dropped on a valid DD target
57455          * @param {Grid} this
57456          * @param {Roo.GridDD} dd The drag drop object
57457          * @param {String} targetId The target drag drop object
57458          * @param {event} e The raw browser event
57459          */
57460         "dragdrop" : true,
57461         /**
57462          * @event dragover
57463          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57464          * @param {Grid} this
57465          * @param {Roo.GridDD} dd The drag drop object
57466          * @param {String} targetId The target drag drop object
57467          * @param {event} e The raw browser event
57468          */
57469         "dragover" : true,
57470         /**
57471          * @event dragenter
57472          *  Fires when the dragged row(s) first cross another DD target while being dragged
57473          * @param {Grid} this
57474          * @param {Roo.GridDD} dd The drag drop object
57475          * @param {String} targetId The target drag drop object
57476          * @param {event} e The raw browser event
57477          */
57478         "dragenter" : true,
57479         /**
57480          * @event dragout
57481          * Fires when the dragged row(s) leave another DD target while being dragged
57482          * @param {Grid} this
57483          * @param {Roo.GridDD} dd The drag drop object
57484          * @param {String} targetId The target drag drop object
57485          * @param {event} e The raw browser event
57486          */
57487         "dragout" : true,
57488         /**
57489          * @event rowclass
57490          * Fires when a row is rendered, so you can change add a style to it.
57491          * @param {GridView} gridview   The grid view
57492          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57493          */
57494         'rowclass' : true,
57495
57496         /**
57497          * @event render
57498          * Fires when the grid is rendered
57499          * @param {Grid} grid
57500          */
57501         'render' : true,
57502             /**
57503              * @event select
57504              * Fires when a date is selected
57505              * @param {DatePicker} this
57506              * @param {Date} date The selected date
57507              */
57508         'select': true,
57509         /**
57510              * @event monthchange
57511              * Fires when the displayed month changes 
57512              * @param {DatePicker} this
57513              * @param {Date} date The selected month
57514              */
57515         'monthchange': true,
57516         /**
57517              * @event evententer
57518              * Fires when mouse over an event
57519              * @param {Calendar} this
57520              * @param {event} Event
57521              */
57522         'evententer': true,
57523         /**
57524              * @event eventleave
57525              * Fires when the mouse leaves an
57526              * @param {Calendar} this
57527              * @param {event}
57528              */
57529         'eventleave': true,
57530         /**
57531              * @event eventclick
57532              * Fires when the mouse click an
57533              * @param {Calendar} this
57534              * @param {event}
57535              */
57536         'eventclick': true,
57537         /**
57538              * @event eventrender
57539              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57540              * @param {Calendar} this
57541              * @param {data} data to be modified
57542              */
57543         'eventrender': true
57544         
57545     });
57546
57547     Roo.grid.Grid.superclass.constructor.call(this);
57548     this.on('render', function() {
57549         this.view.el.addClass('x-grid-cal'); 
57550         
57551         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57552
57553     },this);
57554     
57555     if (!Roo.grid.Calendar.style) {
57556         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57557             
57558             
57559             '.x-grid-cal .x-grid-col' :  {
57560                 height: 'auto !important',
57561                 'vertical-align': 'top'
57562             },
57563             '.x-grid-cal  .fc-event-hori' : {
57564                 height: '14px'
57565             }
57566              
57567             
57568         }, Roo.id());
57569     }
57570
57571     
57572     
57573 };
57574 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57575     /**
57576      * @cfg {Store} eventStore The store that loads events.
57577      */
57578     eventStore : 25,
57579
57580      
57581     activeDate : false,
57582     startDay : 0,
57583     autoWidth : true,
57584     monitorWindowResize : false,
57585
57586     
57587     resizeColumns : function() {
57588         var col = (this.view.el.getWidth() / 7) - 3;
57589         // loop through cols, and setWidth
57590         for(var i =0 ; i < 7 ; i++){
57591             this.cm.setColumnWidth(i, col);
57592         }
57593     },
57594      setDate :function(date) {
57595         
57596         Roo.log('setDate?');
57597         
57598         this.resizeColumns();
57599         var vd = this.activeDate;
57600         this.activeDate = date;
57601 //        if(vd && this.el){
57602 //            var t = date.getTime();
57603 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57604 //                Roo.log('using add remove');
57605 //                
57606 //                this.fireEvent('monthchange', this, date);
57607 //                
57608 //                this.cells.removeClass("fc-state-highlight");
57609 //                this.cells.each(function(c){
57610 //                   if(c.dateValue == t){
57611 //                       c.addClass("fc-state-highlight");
57612 //                       setTimeout(function(){
57613 //                            try{c.dom.firstChild.focus();}catch(e){}
57614 //                       }, 50);
57615 //                       return false;
57616 //                   }
57617 //                   return true;
57618 //                });
57619 //                return;
57620 //            }
57621 //        }
57622         
57623         var days = date.getDaysInMonth();
57624         
57625         var firstOfMonth = date.getFirstDateOfMonth();
57626         var startingPos = firstOfMonth.getDay()-this.startDay;
57627         
57628         if(startingPos < this.startDay){
57629             startingPos += 7;
57630         }
57631         
57632         var pm = date.add(Date.MONTH, -1);
57633         var prevStart = pm.getDaysInMonth()-startingPos;
57634 //        
57635         
57636         
57637         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57638         
57639         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57640         //this.cells.addClassOnOver('fc-state-hover');
57641         
57642         var cells = this.cells.elements;
57643         var textEls = this.textNodes;
57644         
57645         //Roo.each(cells, function(cell){
57646         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57647         //});
57648         
57649         days += startingPos;
57650
57651         // convert everything to numbers so it's fast
57652         var day = 86400000;
57653         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57654         //Roo.log(d);
57655         //Roo.log(pm);
57656         //Roo.log(prevStart);
57657         
57658         var today = new Date().clearTime().getTime();
57659         var sel = date.clearTime().getTime();
57660         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57661         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57662         var ddMatch = this.disabledDatesRE;
57663         var ddText = this.disabledDatesText;
57664         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57665         var ddaysText = this.disabledDaysText;
57666         var format = this.format;
57667         
57668         var setCellClass = function(cal, cell){
57669             
57670             //Roo.log('set Cell Class');
57671             cell.title = "";
57672             var t = d.getTime();
57673             
57674             //Roo.log(d);
57675             
57676             
57677             cell.dateValue = t;
57678             if(t == today){
57679                 cell.className += " fc-today";
57680                 cell.className += " fc-state-highlight";
57681                 cell.title = cal.todayText;
57682             }
57683             if(t == sel){
57684                 // disable highlight in other month..
57685                 cell.className += " fc-state-highlight";
57686                 
57687             }
57688             // disabling
57689             if(t < min) {
57690                 //cell.className = " fc-state-disabled";
57691                 cell.title = cal.minText;
57692                 return;
57693             }
57694             if(t > max) {
57695                 //cell.className = " fc-state-disabled";
57696                 cell.title = cal.maxText;
57697                 return;
57698             }
57699             if(ddays){
57700                 if(ddays.indexOf(d.getDay()) != -1){
57701                     // cell.title = ddaysText;
57702                    // cell.className = " fc-state-disabled";
57703                 }
57704             }
57705             if(ddMatch && format){
57706                 var fvalue = d.dateFormat(format);
57707                 if(ddMatch.test(fvalue)){
57708                     cell.title = ddText.replace("%0", fvalue);
57709                    cell.className = " fc-state-disabled";
57710                 }
57711             }
57712             
57713             if (!cell.initialClassName) {
57714                 cell.initialClassName = cell.dom.className;
57715             }
57716             
57717             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57718         };
57719
57720         var i = 0;
57721         
57722         for(; i < startingPos; i++) {
57723             cells[i].dayName =  (++prevStart);
57724             Roo.log(textEls[i]);
57725             d.setDate(d.getDate()+1);
57726             
57727             //cells[i].className = "fc-past fc-other-month";
57728             setCellClass(this, cells[i]);
57729         }
57730         
57731         var intDay = 0;
57732         
57733         for(; i < days; i++){
57734             intDay = i - startingPos + 1;
57735             cells[i].dayName =  (intDay);
57736             d.setDate(d.getDate()+1);
57737             
57738             cells[i].className = ''; // "x-date-active";
57739             setCellClass(this, cells[i]);
57740         }
57741         var extraDays = 0;
57742         
57743         for(; i < 42; i++) {
57744             //textEls[i].innerHTML = (++extraDays);
57745             
57746             d.setDate(d.getDate()+1);
57747             cells[i].dayName = (++extraDays);
57748             cells[i].className = "fc-future fc-other-month";
57749             setCellClass(this, cells[i]);
57750         }
57751         
57752         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57753         
57754         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57755         
57756         // this will cause all the cells to mis
57757         var rows= [];
57758         var i =0;
57759         for (var r = 0;r < 6;r++) {
57760             for (var c =0;c < 7;c++) {
57761                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57762             }    
57763         }
57764         
57765         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57766         for(i=0;i<cells.length;i++) {
57767             
57768             this.cells.elements[i].dayName = cells[i].dayName ;
57769             this.cells.elements[i].className = cells[i].className;
57770             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57771             this.cells.elements[i].title = cells[i].title ;
57772             this.cells.elements[i].dateValue = cells[i].dateValue ;
57773         }
57774         
57775         
57776         
57777         
57778         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57779         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57780         
57781         ////if(totalRows != 6){
57782             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57783            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57784        // }
57785         
57786         this.fireEvent('monthchange', this, date);
57787         
57788         
57789     },
57790  /**
57791      * Returns the grid's SelectionModel.
57792      * @return {SelectionModel}
57793      */
57794     getSelectionModel : function(){
57795         if(!this.selModel){
57796             this.selModel = new Roo.grid.CellSelectionModel();
57797         }
57798         return this.selModel;
57799     },
57800
57801     load: function() {
57802         this.eventStore.load()
57803         
57804         
57805         
57806     },
57807     
57808     findCell : function(dt) {
57809         dt = dt.clearTime().getTime();
57810         var ret = false;
57811         this.cells.each(function(c){
57812             //Roo.log("check " +c.dateValue + '?=' + dt);
57813             if(c.dateValue == dt){
57814                 ret = c;
57815                 return false;
57816             }
57817             return true;
57818         });
57819         
57820         return ret;
57821     },
57822     
57823     findCells : function(rec) {
57824         var s = rec.data.start_dt.clone().clearTime().getTime();
57825        // Roo.log(s);
57826         var e= rec.data.end_dt.clone().clearTime().getTime();
57827        // Roo.log(e);
57828         var ret = [];
57829         this.cells.each(function(c){
57830              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57831             
57832             if(c.dateValue > e){
57833                 return ;
57834             }
57835             if(c.dateValue < s){
57836                 return ;
57837             }
57838             ret.push(c);
57839         });
57840         
57841         return ret;    
57842     },
57843     
57844     findBestRow: function(cells)
57845     {
57846         var ret = 0;
57847         
57848         for (var i =0 ; i < cells.length;i++) {
57849             ret  = Math.max(cells[i].rows || 0,ret);
57850         }
57851         return ret;
57852         
57853     },
57854     
57855     
57856     addItem : function(rec)
57857     {
57858         // look for vertical location slot in
57859         var cells = this.findCells(rec);
57860         
57861         rec.row = this.findBestRow(cells);
57862         
57863         // work out the location.
57864         
57865         var crow = false;
57866         var rows = [];
57867         for(var i =0; i < cells.length; i++) {
57868             if (!crow) {
57869                 crow = {
57870                     start : cells[i],
57871                     end :  cells[i]
57872                 };
57873                 continue;
57874             }
57875             if (crow.start.getY() == cells[i].getY()) {
57876                 // on same row.
57877                 crow.end = cells[i];
57878                 continue;
57879             }
57880             // different row.
57881             rows.push(crow);
57882             crow = {
57883                 start: cells[i],
57884                 end : cells[i]
57885             };
57886             
57887         }
57888         
57889         rows.push(crow);
57890         rec.els = [];
57891         rec.rows = rows;
57892         rec.cells = cells;
57893         for (var i = 0; i < cells.length;i++) {
57894             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57895             
57896         }
57897         
57898         
57899     },
57900     
57901     clearEvents: function() {
57902         
57903         if (!this.eventStore.getCount()) {
57904             return;
57905         }
57906         // reset number of rows in cells.
57907         Roo.each(this.cells.elements, function(c){
57908             c.rows = 0;
57909         });
57910         
57911         this.eventStore.each(function(e) {
57912             this.clearEvent(e);
57913         },this);
57914         
57915     },
57916     
57917     clearEvent : function(ev)
57918     {
57919         if (ev.els) {
57920             Roo.each(ev.els, function(el) {
57921                 el.un('mouseenter' ,this.onEventEnter, this);
57922                 el.un('mouseleave' ,this.onEventLeave, this);
57923                 el.remove();
57924             },this);
57925             ev.els = [];
57926         }
57927     },
57928     
57929     
57930     renderEvent : function(ev,ctr) {
57931         if (!ctr) {
57932              ctr = this.view.el.select('.fc-event-container',true).first();
57933         }
57934         
57935          
57936         this.clearEvent(ev);
57937             //code
57938        
57939         
57940         
57941         ev.els = [];
57942         var cells = ev.cells;
57943         var rows = ev.rows;
57944         this.fireEvent('eventrender', this, ev);
57945         
57946         for(var i =0; i < rows.length; i++) {
57947             
57948             cls = '';
57949             if (i == 0) {
57950                 cls += ' fc-event-start';
57951             }
57952             if ((i+1) == rows.length) {
57953                 cls += ' fc-event-end';
57954             }
57955             
57956             //Roo.log(ev.data);
57957             // how many rows should it span..
57958             var cg = this.eventTmpl.append(ctr,Roo.apply({
57959                 fccls : cls
57960                 
57961             }, ev.data) , true);
57962             
57963             
57964             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57965             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57966             cg.on('click', this.onEventClick, this, ev);
57967             
57968             ev.els.push(cg);
57969             
57970             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57971             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57972             //Roo.log(cg);
57973              
57974             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57975             cg.setWidth(ebox.right - sbox.x -2);
57976         }
57977     },
57978     
57979     renderEvents: function()
57980     {   
57981         // first make sure there is enough space..
57982         
57983         if (!this.eventTmpl) {
57984             this.eventTmpl = new Roo.Template(
57985                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57986                     '<div class="fc-event-inner">' +
57987                         '<span class="fc-event-time">{time}</span>' +
57988                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57989                     '</div>' +
57990                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57991                 '</div>'
57992             );
57993                 
57994         }
57995                
57996         
57997         
57998         this.cells.each(function(c) {
57999             //Roo.log(c.select('.fc-day-content div',true).first());
58000             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
58001         });
58002         
58003         var ctr = this.view.el.select('.fc-event-container',true).first();
58004         
58005         var cls;
58006         this.eventStore.each(function(ev){
58007             
58008             this.renderEvent(ev);
58009              
58010              
58011         }, this);
58012         this.view.layout();
58013         
58014     },
58015     
58016     onEventEnter: function (e, el,event,d) {
58017         this.fireEvent('evententer', this, el, event);
58018     },
58019     
58020     onEventLeave: function (e, el,event,d) {
58021         this.fireEvent('eventleave', this, el, event);
58022     },
58023     
58024     onEventClick: function (e, el,event,d) {
58025         this.fireEvent('eventclick', this, el, event);
58026     },
58027     
58028     onMonthChange: function () {
58029         this.store.load();
58030     },
58031     
58032     onLoad: function () {
58033         
58034         //Roo.log('calendar onload');
58035 //         
58036         if(this.eventStore.getCount() > 0){
58037             
58038            
58039             
58040             this.eventStore.each(function(d){
58041                 
58042                 
58043                 // FIXME..
58044                 var add =   d.data;
58045                 if (typeof(add.end_dt) == 'undefined')  {
58046                     Roo.log("Missing End time in calendar data: ");
58047                     Roo.log(d);
58048                     return;
58049                 }
58050                 if (typeof(add.start_dt) == 'undefined')  {
58051                     Roo.log("Missing Start time in calendar data: ");
58052                     Roo.log(d);
58053                     return;
58054                 }
58055                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58056                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58057                 add.id = add.id || d.id;
58058                 add.title = add.title || '??';
58059                 
58060                 this.addItem(d);
58061                 
58062              
58063             },this);
58064         }
58065         
58066         this.renderEvents();
58067     }
58068     
58069
58070 });
58071 /*
58072  grid : {
58073                 xtype: 'Grid',
58074                 xns: Roo.grid,
58075                 listeners : {
58076                     render : function ()
58077                     {
58078                         _this.grid = this;
58079                         
58080                         if (!this.view.el.hasClass('course-timesheet')) {
58081                             this.view.el.addClass('course-timesheet');
58082                         }
58083                         if (this.tsStyle) {
58084                             this.ds.load({});
58085                             return; 
58086                         }
58087                         Roo.log('width');
58088                         Roo.log(_this.grid.view.el.getWidth());
58089                         
58090                         
58091                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58092                             '.course-timesheet .x-grid-row' : {
58093                                 height: '80px'
58094                             },
58095                             '.x-grid-row td' : {
58096                                 'vertical-align' : 0
58097                             },
58098                             '.course-edit-link' : {
58099                                 'color' : 'blue',
58100                                 'text-overflow' : 'ellipsis',
58101                                 'overflow' : 'hidden',
58102                                 'white-space' : 'nowrap',
58103                                 'cursor' : 'pointer'
58104                             },
58105                             '.sub-link' : {
58106                                 'color' : 'green'
58107                             },
58108                             '.de-act-sup-link' : {
58109                                 'color' : 'purple',
58110                                 'text-decoration' : 'line-through'
58111                             },
58112                             '.de-act-link' : {
58113                                 'color' : 'red',
58114                                 'text-decoration' : 'line-through'
58115                             },
58116                             '.course-timesheet .course-highlight' : {
58117                                 'border-top-style': 'dashed !important',
58118                                 'border-bottom-bottom': 'dashed !important'
58119                             },
58120                             '.course-timesheet .course-item' : {
58121                                 'font-family'   : 'tahoma, arial, helvetica',
58122                                 'font-size'     : '11px',
58123                                 'overflow'      : 'hidden',
58124                                 'padding-left'  : '10px',
58125                                 'padding-right' : '10px',
58126                                 'padding-top' : '10px' 
58127                             }
58128                             
58129                         }, Roo.id());
58130                                 this.ds.load({});
58131                     }
58132                 },
58133                 autoWidth : true,
58134                 monitorWindowResize : false,
58135                 cellrenderer : function(v,x,r)
58136                 {
58137                     return v;
58138                 },
58139                 sm : {
58140                     xtype: 'CellSelectionModel',
58141                     xns: Roo.grid
58142                 },
58143                 dataSource : {
58144                     xtype: 'Store',
58145                     xns: Roo.data,
58146                     listeners : {
58147                         beforeload : function (_self, options)
58148                         {
58149                             options.params = options.params || {};
58150                             options.params._month = _this.monthField.getValue();
58151                             options.params.limit = 9999;
58152                             options.params['sort'] = 'when_dt';    
58153                             options.params['dir'] = 'ASC';    
58154                             this.proxy.loadResponse = this.loadResponse;
58155                             Roo.log("load?");
58156                             //this.addColumns();
58157                         },
58158                         load : function (_self, records, options)
58159                         {
58160                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58161                                 // if you click on the translation.. you can edit it...
58162                                 var el = Roo.get(this);
58163                                 var id = el.dom.getAttribute('data-id');
58164                                 var d = el.dom.getAttribute('data-date');
58165                                 var t = el.dom.getAttribute('data-time');
58166                                 //var id = this.child('span').dom.textContent;
58167                                 
58168                                 //Roo.log(this);
58169                                 Pman.Dialog.CourseCalendar.show({
58170                                     id : id,
58171                                     when_d : d,
58172                                     when_t : t,
58173                                     productitem_active : id ? 1 : 0
58174                                 }, function() {
58175                                     _this.grid.ds.load({});
58176                                 });
58177                            
58178                            });
58179                            
58180                            _this.panel.fireEvent('resize', [ '', '' ]);
58181                         }
58182                     },
58183                     loadResponse : function(o, success, response){
58184                             // this is overridden on before load..
58185                             
58186                             Roo.log("our code?");       
58187                             //Roo.log(success);
58188                             //Roo.log(response)
58189                             delete this.activeRequest;
58190                             if(!success){
58191                                 this.fireEvent("loadexception", this, o, response);
58192                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58193                                 return;
58194                             }
58195                             var result;
58196                             try {
58197                                 result = o.reader.read(response);
58198                             }catch(e){
58199                                 Roo.log("load exception?");
58200                                 this.fireEvent("loadexception", this, o, response, e);
58201                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58202                                 return;
58203                             }
58204                             Roo.log("ready...");        
58205                             // loop through result.records;
58206                             // and set this.tdate[date] = [] << array of records..
58207                             _this.tdata  = {};
58208                             Roo.each(result.records, function(r){
58209                                 //Roo.log(r.data);
58210                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58211                                     _this.tdata[r.data.when_dt.format('j')] = [];
58212                                 }
58213                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58214                             });
58215                             
58216                             //Roo.log(_this.tdata);
58217                             
58218                             result.records = [];
58219                             result.totalRecords = 6;
58220                     
58221                             // let's generate some duumy records for the rows.
58222                             //var st = _this.dateField.getValue();
58223                             
58224                             // work out monday..
58225                             //st = st.add(Date.DAY, -1 * st.format('w'));
58226                             
58227                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58228                             
58229                             var firstOfMonth = date.getFirstDayOfMonth();
58230                             var days = date.getDaysInMonth();
58231                             var d = 1;
58232                             var firstAdded = false;
58233                             for (var i = 0; i < result.totalRecords ; i++) {
58234                                 //var d= st.add(Date.DAY, i);
58235                                 var row = {};
58236                                 var added = 0;
58237                                 for(var w = 0 ; w < 7 ; w++){
58238                                     if(!firstAdded && firstOfMonth != w){
58239                                         continue;
58240                                     }
58241                                     if(d > days){
58242                                         continue;
58243                                     }
58244                                     firstAdded = true;
58245                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58246                                     row['weekday'+w] = String.format(
58247                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58248                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58249                                                     d,
58250                                                     date.format('Y-m-')+dd
58251                                                 );
58252                                     added++;
58253                                     if(typeof(_this.tdata[d]) != 'undefined'){
58254                                         Roo.each(_this.tdata[d], function(r){
58255                                             var is_sub = '';
58256                                             var deactive = '';
58257                                             var id = r.id;
58258                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58259                                             if(r.parent_id*1>0){
58260                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58261                                                 id = r.parent_id;
58262                                             }
58263                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58264                                                 deactive = 'de-act-link';
58265                                             }
58266                                             
58267                                             row['weekday'+w] += String.format(
58268                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58269                                                     id, //0
58270                                                     r.product_id_name, //1
58271                                                     r.when_dt.format('h:ia'), //2
58272                                                     is_sub, //3
58273                                                     deactive, //4
58274                                                     desc // 5
58275                                             );
58276                                         });
58277                                     }
58278                                     d++;
58279                                 }
58280                                 
58281                                 // only do this if something added..
58282                                 if(added > 0){ 
58283                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58284                                 }
58285                                 
58286                                 
58287                                 // push it twice. (second one with an hour..
58288                                 
58289                             }
58290                             //Roo.log(result);
58291                             this.fireEvent("load", this, o, o.request.arg);
58292                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58293                         },
58294                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58295                     proxy : {
58296                         xtype: 'HttpProxy',
58297                         xns: Roo.data,
58298                         method : 'GET',
58299                         url : baseURL + '/Roo/Shop_course.php'
58300                     },
58301                     reader : {
58302                         xtype: 'JsonReader',
58303                         xns: Roo.data,
58304                         id : 'id',
58305                         fields : [
58306                             {
58307                                 'name': 'id',
58308                                 'type': 'int'
58309                             },
58310                             {
58311                                 'name': 'when_dt',
58312                                 'type': 'string'
58313                             },
58314                             {
58315                                 'name': 'end_dt',
58316                                 'type': 'string'
58317                             },
58318                             {
58319                                 'name': 'parent_id',
58320                                 'type': 'int'
58321                             },
58322                             {
58323                                 'name': 'product_id',
58324                                 'type': 'int'
58325                             },
58326                             {
58327                                 'name': 'productitem_id',
58328                                 'type': 'int'
58329                             },
58330                             {
58331                                 'name': 'guid',
58332                                 'type': 'int'
58333                             }
58334                         ]
58335                     }
58336                 },
58337                 toolbar : {
58338                     xtype: 'Toolbar',
58339                     xns: Roo,
58340                     items : [
58341                         {
58342                             xtype: 'Button',
58343                             xns: Roo.Toolbar,
58344                             listeners : {
58345                                 click : function (_self, e)
58346                                 {
58347                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58348                                     sd.setMonth(sd.getMonth()-1);
58349                                     _this.monthField.setValue(sd.format('Y-m-d'));
58350                                     _this.grid.ds.load({});
58351                                 }
58352                             },
58353                             text : "Back"
58354                         },
58355                         {
58356                             xtype: 'Separator',
58357                             xns: Roo.Toolbar
58358                         },
58359                         {
58360                             xtype: 'MonthField',
58361                             xns: Roo.form,
58362                             listeners : {
58363                                 render : function (_self)
58364                                 {
58365                                     _this.monthField = _self;
58366                                    // _this.monthField.set  today
58367                                 },
58368                                 select : function (combo, date)
58369                                 {
58370                                     _this.grid.ds.load({});
58371                                 }
58372                             },
58373                             value : (function() { return new Date(); })()
58374                         },
58375                         {
58376                             xtype: 'Separator',
58377                             xns: Roo.Toolbar
58378                         },
58379                         {
58380                             xtype: 'TextItem',
58381                             xns: Roo.Toolbar,
58382                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58383                         },
58384                         {
58385                             xtype: 'Fill',
58386                             xns: Roo.Toolbar
58387                         },
58388                         {
58389                             xtype: 'Button',
58390                             xns: Roo.Toolbar,
58391                             listeners : {
58392                                 click : function (_self, e)
58393                                 {
58394                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58395                                     sd.setMonth(sd.getMonth()+1);
58396                                     _this.monthField.setValue(sd.format('Y-m-d'));
58397                                     _this.grid.ds.load({});
58398                                 }
58399                             },
58400                             text : "Next"
58401                         }
58402                     ]
58403                 },
58404                  
58405             }
58406         };
58407         
58408         *//*
58409  * Based on:
58410  * Ext JS Library 1.1.1
58411  * Copyright(c) 2006-2007, Ext JS, LLC.
58412  *
58413  * Originally Released Under LGPL - original licence link has changed is not relivant.
58414  *
58415  * Fork - LGPL
58416  * <script type="text/javascript">
58417  */
58418  
58419 /**
58420  * @class Roo.LoadMask
58421  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58422  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58423  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58424  * element's UpdateManager load indicator and will be destroyed after the initial load.
58425  * @constructor
58426  * Create a new LoadMask
58427  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58428  * @param {Object} config The config object
58429  */
58430 Roo.LoadMask = function(el, config){
58431     this.el = Roo.get(el);
58432     Roo.apply(this, config);
58433     if(this.store){
58434         this.store.on('beforeload', this.onBeforeLoad, this);
58435         this.store.on('load', this.onLoad, this);
58436         this.store.on('loadexception', this.onLoadException, this);
58437         this.removeMask = false;
58438     }else{
58439         var um = this.el.getUpdateManager();
58440         um.showLoadIndicator = false; // disable the default indicator
58441         um.on('beforeupdate', this.onBeforeLoad, this);
58442         um.on('update', this.onLoad, this);
58443         um.on('failure', this.onLoad, this);
58444         this.removeMask = true;
58445     }
58446 };
58447
58448 Roo.LoadMask.prototype = {
58449     /**
58450      * @cfg {Boolean} removeMask
58451      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58452      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58453      */
58454     /**
58455      * @cfg {String} msg
58456      * The text to display in a centered loading message box (defaults to 'Loading...')
58457      */
58458     msg : 'Loading...',
58459     /**
58460      * @cfg {String} msgCls
58461      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58462      */
58463     msgCls : 'x-mask-loading',
58464
58465     /**
58466      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58467      * @type Boolean
58468      */
58469     disabled: false,
58470
58471     /**
58472      * Disables the mask to prevent it from being displayed
58473      */
58474     disable : function(){
58475        this.disabled = true;
58476     },
58477
58478     /**
58479      * Enables the mask so that it can be displayed
58480      */
58481     enable : function(){
58482         this.disabled = false;
58483     },
58484     
58485     onLoadException : function()
58486     {
58487         Roo.log(arguments);
58488         
58489         if (typeof(arguments[3]) != 'undefined') {
58490             Roo.MessageBox.alert("Error loading",arguments[3]);
58491         } 
58492         /*
58493         try {
58494             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58495                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58496             }   
58497         } catch(e) {
58498             
58499         }
58500         */
58501     
58502         
58503         
58504         this.el.unmask(this.removeMask);
58505     },
58506     // private
58507     onLoad : function()
58508     {
58509         this.el.unmask(this.removeMask);
58510     },
58511
58512     // private
58513     onBeforeLoad : function(){
58514         if(!this.disabled){
58515             this.el.mask(this.msg, this.msgCls);
58516         }
58517     },
58518
58519     // private
58520     destroy : function(){
58521         if(this.store){
58522             this.store.un('beforeload', this.onBeforeLoad, this);
58523             this.store.un('load', this.onLoad, this);
58524             this.store.un('loadexception', this.onLoadException, this);
58525         }else{
58526             var um = this.el.getUpdateManager();
58527             um.un('beforeupdate', this.onBeforeLoad, this);
58528             um.un('update', this.onLoad, this);
58529             um.un('failure', this.onLoad, this);
58530         }
58531     }
58532 };/*
58533  * Based on:
58534  * Ext JS Library 1.1.1
58535  * Copyright(c) 2006-2007, Ext JS, LLC.
58536  *
58537  * Originally Released Under LGPL - original licence link has changed is not relivant.
58538  *
58539  * Fork - LGPL
58540  * <script type="text/javascript">
58541  */
58542
58543
58544 /**
58545  * @class Roo.XTemplate
58546  * @extends Roo.Template
58547  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58548 <pre><code>
58549 var t = new Roo.XTemplate(
58550         '&lt;select name="{name}"&gt;',
58551                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58552         '&lt;/select&gt;'
58553 );
58554  
58555 // then append, applying the master template values
58556  </code></pre>
58557  *
58558  * Supported features:
58559  *
58560  *  Tags:
58561
58562 <pre><code>
58563       {a_variable} - output encoded.
58564       {a_variable.format:("Y-m-d")} - call a method on the variable
58565       {a_variable:raw} - unencoded output
58566       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58567       {a_variable:this.method_on_template(...)} - call a method on the template object.
58568  
58569 </code></pre>
58570  *  The tpl tag:
58571 <pre><code>
58572         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58573         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58574         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58575         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58576   
58577         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58578         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58579 </code></pre>
58580  *      
58581  */
58582 Roo.XTemplate = function()
58583 {
58584     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58585     if (this.html) {
58586         this.compile();
58587     }
58588 };
58589
58590
58591 Roo.extend(Roo.XTemplate, Roo.Template, {
58592
58593     /**
58594      * The various sub templates
58595      */
58596     tpls : false,
58597     /**
58598      *
58599      * basic tag replacing syntax
58600      * WORD:WORD()
58601      *
58602      * // you can fake an object call by doing this
58603      *  x.t:(test,tesT) 
58604      * 
58605      */
58606     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58607
58608     /**
58609      * compile the template
58610      *
58611      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58612      *
58613      */
58614     compile: function()
58615     {
58616         var s = this.html;
58617      
58618         s = ['<tpl>', s, '</tpl>'].join('');
58619     
58620         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58621             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58622             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58623             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58624             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58625             m,
58626             id     = 0,
58627             tpls   = [];
58628     
58629         while(true == !!(m = s.match(re))){
58630             var forMatch   = m[0].match(nameRe),
58631                 ifMatch   = m[0].match(ifRe),
58632                 execMatch   = m[0].match(execRe),
58633                 namedMatch   = m[0].match(namedRe),
58634                 
58635                 exp  = null, 
58636                 fn   = null,
58637                 exec = null,
58638                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58639                 
58640             if (ifMatch) {
58641                 // if - puts fn into test..
58642                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58643                 if(exp){
58644                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58645                 }
58646             }
58647             
58648             if (execMatch) {
58649                 // exec - calls a function... returns empty if true is  returned.
58650                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58651                 if(exp){
58652                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58653                 }
58654             }
58655             
58656             
58657             if (name) {
58658                 // for = 
58659                 switch(name){
58660                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58661                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58662                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58663                 }
58664             }
58665             var uid = namedMatch ? namedMatch[1] : id;
58666             
58667             
58668             tpls.push({
58669                 id:     namedMatch ? namedMatch[1] : id,
58670                 target: name,
58671                 exec:   exec,
58672                 test:   fn,
58673                 body:   m[1] || ''
58674             });
58675             if (namedMatch) {
58676                 s = s.replace(m[0], '');
58677             } else { 
58678                 s = s.replace(m[0], '{xtpl'+ id + '}');
58679             }
58680             ++id;
58681         }
58682         this.tpls = [];
58683         for(var i = tpls.length-1; i >= 0; --i){
58684             this.compileTpl(tpls[i]);
58685             this.tpls[tpls[i].id] = tpls[i];
58686         }
58687         this.master = tpls[tpls.length-1];
58688         return this;
58689     },
58690     /**
58691      * same as applyTemplate, except it's done to one of the subTemplates
58692      * when using named templates, you can do:
58693      *
58694      * var str = pl.applySubTemplate('your-name', values);
58695      *
58696      * 
58697      * @param {Number} id of the template
58698      * @param {Object} values to apply to template
58699      * @param {Object} parent (normaly the instance of this object)
58700      */
58701     applySubTemplate : function(id, values, parent)
58702     {
58703         
58704         
58705         var t = this.tpls[id];
58706         
58707         
58708         try { 
58709             if(t.test && !t.test.call(this, values, parent)){
58710                 return '';
58711             }
58712         } catch(e) {
58713             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58714             Roo.log(e.toString());
58715             Roo.log(t.test);
58716             return ''
58717         }
58718         try { 
58719             
58720             if(t.exec && t.exec.call(this, values, parent)){
58721                 return '';
58722             }
58723         } catch(e) {
58724             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58725             Roo.log(e.toString());
58726             Roo.log(t.exec);
58727             return ''
58728         }
58729         try {
58730             var vs = t.target ? t.target.call(this, values, parent) : values;
58731             parent = t.target ? values : parent;
58732             if(t.target && vs instanceof Array){
58733                 var buf = [];
58734                 for(var i = 0, len = vs.length; i < len; i++){
58735                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58736                 }
58737                 return buf.join('');
58738             }
58739             return t.compiled.call(this, vs, parent);
58740         } catch (e) {
58741             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58742             Roo.log(e.toString());
58743             Roo.log(t.compiled);
58744             return '';
58745         }
58746     },
58747
58748     compileTpl : function(tpl)
58749     {
58750         var fm = Roo.util.Format;
58751         var useF = this.disableFormats !== true;
58752         var sep = Roo.isGecko ? "+" : ",";
58753         var undef = function(str) {
58754             Roo.log("Property not found :"  + str);
58755             return '';
58756         };
58757         
58758         var fn = function(m, name, format, args)
58759         {
58760             //Roo.log(arguments);
58761             args = args ? args.replace(/\\'/g,"'") : args;
58762             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58763             if (typeof(format) == 'undefined') {
58764                 format= 'htmlEncode';
58765             }
58766             if (format == 'raw' ) {
58767                 format = false;
58768             }
58769             
58770             if(name.substr(0, 4) == 'xtpl'){
58771                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58772             }
58773             
58774             // build an array of options to determine if value is undefined..
58775             
58776             // basically get 'xxxx.yyyy' then do
58777             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58778             //    (function () { Roo.log("Property not found"); return ''; })() :
58779             //    ......
58780             
58781             var udef_ar = [];
58782             var lookfor = '';
58783             Roo.each(name.split('.'), function(st) {
58784                 lookfor += (lookfor.length ? '.': '') + st;
58785                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58786             });
58787             
58788             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58789             
58790             
58791             if(format && useF){
58792                 
58793                 args = args ? ',' + args : "";
58794                  
58795                 if(format.substr(0, 5) != "this."){
58796                     format = "fm." + format + '(';
58797                 }else{
58798                     format = 'this.call("'+ format.substr(5) + '", ';
58799                     args = ", values";
58800                 }
58801                 
58802                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58803             }
58804              
58805             if (args.length) {
58806                 // called with xxyx.yuu:(test,test)
58807                 // change to ()
58808                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58809             }
58810             // raw.. - :raw modifier..
58811             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58812             
58813         };
58814         var body;
58815         // branched to use + in gecko and [].join() in others
58816         if(Roo.isGecko){
58817             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58818                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58819                     "';};};";
58820         }else{
58821             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58822             body.push(tpl.body.replace(/(\r\n|\n)/g,
58823                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58824             body.push("'].join('');};};");
58825             body = body.join('');
58826         }
58827         
58828         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58829        
58830         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58831         eval(body);
58832         
58833         return this;
58834     },
58835
58836     applyTemplate : function(values){
58837         return this.master.compiled.call(this, values, {});
58838         //var s = this.subs;
58839     },
58840
58841     apply : function(){
58842         return this.applyTemplate.apply(this, arguments);
58843     }
58844
58845  });
58846
58847 Roo.XTemplate.from = function(el){
58848     el = Roo.getDom(el);
58849     return new Roo.XTemplate(el.value || el.innerHTML);
58850 };