roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * 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.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * 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]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         }
672         
673     });
674
675
676 })();
677
678 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
679                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
680                 "Roo.app", "Roo.ux",
681                 "Roo.bootstrap",
682                 "Roo.bootstrap.dash");
683 /*
684  * Based on:
685  * Ext JS Library 1.1.1
686  * Copyright(c) 2006-2007, Ext JS, LLC.
687  *
688  * Originally Released Under LGPL - original licence link has changed is not relivant.
689  *
690  * Fork - LGPL
691  * <script type="text/javascript">
692  */
693
694 (function() {    
695     // wrappedn so fnCleanup is not in global scope...
696     if(Roo.isIE) {
697         function fnCleanUp() {
698             var p = Function.prototype;
699             delete p.createSequence;
700             delete p.defer;
701             delete p.createDelegate;
702             delete p.createCallback;
703             delete p.createInterceptor;
704
705             window.detachEvent("onunload", fnCleanUp);
706         }
707         window.attachEvent("onunload", fnCleanUp);
708     }
709 })();
710
711
712 /**
713  * @class Function
714  * These functions are available on every Function object (any JavaScript function).
715  */
716 Roo.apply(Function.prototype, {
717      /**
718      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
719      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
720      * Will create a function that is bound to those 2 args.
721      * @return {Function} The new function
722     */
723     createCallback : function(/*args...*/){
724         // make args available, in function below
725         var args = arguments;
726         var method = this;
727         return function() {
728             return method.apply(window, args);
729         };
730     },
731
732     /**
733      * Creates a delegate (callback) that sets the scope to obj.
734      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
735      * Will create a function that is automatically scoped to this.
736      * @param {Object} obj (optional) The object for which the scope is set
737      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
738      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
739      *                                             if a number the args are inserted at the specified position
740      * @return {Function} The new function
741      */
742     createDelegate : function(obj, args, appendArgs){
743         var method = this;
744         return function() {
745             var callArgs = args || arguments;
746             if(appendArgs === true){
747                 callArgs = Array.prototype.slice.call(arguments, 0);
748                 callArgs = callArgs.concat(args);
749             }else if(typeof appendArgs == "number"){
750                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
751                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
752                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
753             }
754             return method.apply(obj || window, callArgs);
755         };
756     },
757
758     /**
759      * Calls this function after the number of millseconds specified.
760      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
761      * @param {Object} obj (optional) The object for which the scope is set
762      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
763      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
764      *                                             if a number the args are inserted at the specified position
765      * @return {Number} The timeout id that can be used with clearTimeout
766      */
767     defer : function(millis, obj, args, appendArgs){
768         var fn = this.createDelegate(obj, args, appendArgs);
769         if(millis){
770             return setTimeout(fn, millis);
771         }
772         fn();
773         return 0;
774     },
775     /**
776      * Create a combined function call sequence of the original function + the passed function.
777      * The resulting function returns the results of the original function.
778      * The passed fcn is called with the parameters of the original function
779      * @param {Function} fcn The function to sequence
780      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
781      * @return {Function} The new function
782      */
783     createSequence : function(fcn, scope){
784         if(typeof fcn != "function"){
785             return this;
786         }
787         var method = this;
788         return function() {
789             var retval = method.apply(this || window, arguments);
790             fcn.apply(scope || this || window, arguments);
791             return retval;
792         };
793     },
794
795     /**
796      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
797      * The resulting function returns the results of the original function.
798      * The passed fcn is called with the parameters of the original function.
799      * @addon
800      * @param {Function} fcn The function to call before the original
801      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
802      * @return {Function} The new function
803      */
804     createInterceptor : function(fcn, scope){
805         if(typeof fcn != "function"){
806             return this;
807         }
808         var method = this;
809         return function() {
810             fcn.target = this;
811             fcn.method = method;
812             if(fcn.apply(scope || this || window, arguments) === false){
813                 return;
814             }
815             return method.apply(this || window, arguments);
816         };
817     }
818 });
819 /*
820  * Based on:
821  * Ext JS Library 1.1.1
822  * Copyright(c) 2006-2007, Ext JS, LLC.
823  *
824  * Originally Released Under LGPL - original licence link has changed is not relivant.
825  *
826  * Fork - LGPL
827  * <script type="text/javascript">
828  */
829
830 Roo.applyIf(String, {
831     
832     /** @scope String */
833     
834     /**
835      * Escapes the passed string for ' and \
836      * @param {String} string The string to escape
837      * @return {String} The escaped string
838      * @static
839      */
840     escape : function(string) {
841         return string.replace(/('|\\)/g, "\\$1");
842     },
843
844     /**
845      * Pads the left side of a string with a specified character.  This is especially useful
846      * for normalizing number and date strings.  Example usage:
847      * <pre><code>
848 var s = String.leftPad('123', 5, '0');
849 // s now contains the string: '00123'
850 </code></pre>
851      * @param {String} string The original string
852      * @param {Number} size The total length of the output string
853      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
854      * @return {String} The padded string
855      * @static
856      */
857     leftPad : function (val, size, ch) {
858         var result = new String(val);
859         if(ch === null || ch === undefined || ch === '') {
860             ch = " ";
861         }
862         while (result.length < size) {
863             result = ch + result;
864         }
865         return result;
866     },
867
868     /**
869      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
870      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
871      * <pre><code>
872 var cls = 'my-class', text = 'Some text';
873 var s = String.format('<div class="{0}">{1}</div>', cls, text);
874 // s now contains the string: '<div class="my-class">Some text</div>'
875 </code></pre>
876      * @param {String} string The tokenized string to be formatted
877      * @param {String} value1 The value to replace token {0}
878      * @param {String} value2 Etc...
879      * @return {String} The formatted string
880      * @static
881      */
882     format : function(format){
883         var args = Array.prototype.slice.call(arguments, 1);
884         return format.replace(/\{(\d+)\}/g, function(m, i){
885             return Roo.util.Format.htmlEncode(args[i]);
886         });
887     }
888   
889     
890 });
891
892 /**
893  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
894  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
895  * they are already different, the first value passed in is returned.  Note that this method returns the new value
896  * but does not change the current string.
897  * <pre><code>
898 // alternate sort directions
899 sort = sort.toggle('ASC', 'DESC');
900
901 // instead of conditional logic:
902 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
903 </code></pre>
904  * @param {String} value The value to compare to the current string
905  * @param {String} other The new value to use if the string already equals the first value passed in
906  * @return {String} The new value
907  */
908  
909 String.prototype.toggle = function(value, other){
910     return this == value ? other : value;
911 };
912
913
914 /**
915   * Remove invalid unicode characters from a string 
916   *
917   * @return {String} The clean string
918   */
919 String.prototype.unicodeClean = function () {
920     return this.replace(/[\s\S]/g,
921         function(character) {
922             if (character.charCodeAt()< 256) {
923               return character;
924            }
925            try {
926                 encodeURIComponent(character);
927            } catch(e) { 
928               return '';
929            }
930            return character;
931         }
932     );
933 };
934   
935 /*
936  * Based on:
937  * Ext JS Library 1.1.1
938  * Copyright(c) 2006-2007, Ext JS, LLC.
939  *
940  * Originally Released Under LGPL - original licence link has changed is not relivant.
941  *
942  * Fork - LGPL
943  * <script type="text/javascript">
944  */
945
946  /**
947  * @class Number
948  */
949 Roo.applyIf(Number.prototype, {
950     /**
951      * Checks whether or not the current number is within a desired range.  If the number is already within the
952      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
953      * exceeded.  Note that this method returns the constrained value but does not change the current number.
954      * @param {Number} min The minimum number in the range
955      * @param {Number} max The maximum number in the range
956      * @return {Number} The constrained value if outside the range, otherwise the current value
957      */
958     constrain : function(min, max){
959         return Math.min(Math.max(this, min), max);
960     }
961 });/*
962  * Based on:
963  * Ext JS Library 1.1.1
964  * Copyright(c) 2006-2007, Ext JS, LLC.
965  *
966  * Originally Released Under LGPL - original licence link has changed is not relivant.
967  *
968  * Fork - LGPL
969  * <script type="text/javascript">
970  */
971  /**
972  * @class Array
973  */
974 Roo.applyIf(Array.prototype, {
975     /**
976      * 
977      * Checks whether or not the specified object exists in the array.
978      * @param {Object} o The object to check for
979      * @return {Number} The index of o in the array (or -1 if it is not found)
980      */
981     indexOf : function(o){
982        for (var i = 0, len = this.length; i < len; i++){
983               if(this[i] == o) { return i; }
984        }
985            return -1;
986     },
987
988     /**
989      * Removes the specified object from the array.  If the object is not found nothing happens.
990      * @param {Object} o The object to remove
991      */
992     remove : function(o){
993        var index = this.indexOf(o);
994        if(index != -1){
995            this.splice(index, 1);
996        }
997     },
998     /**
999      * Map (JS 1.6 compatibility)
1000      * @param {Function} function  to call
1001      */
1002     map : function(fun )
1003     {
1004         var len = this.length >>> 0;
1005         if (typeof fun != "function") {
1006             throw new TypeError();
1007         }
1008         var res = new Array(len);
1009         var thisp = arguments[1];
1010         for (var i = 0; i < len; i++)
1011         {
1012             if (i in this) {
1013                 res[i] = fun.call(thisp, this[i], i, this);
1014             }
1015         }
1016
1017         return res;
1018     }
1019     
1020 });
1021
1022
1023  
1024 /*
1025  * Based on:
1026  * Ext JS Library 1.1.1
1027  * Copyright(c) 2006-2007, Ext JS, LLC.
1028  *
1029  * Originally Released Under LGPL - original licence link has changed is not relivant.
1030  *
1031  * Fork - LGPL
1032  * <script type="text/javascript">
1033  */
1034
1035 /**
1036  * @class Date
1037  *
1038  * The date parsing and format syntax is a subset of
1039  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1040  * supported will provide results equivalent to their PHP versions.
1041  *
1042  * Following is the list of all currently supported formats:
1043  *<pre>
1044 Sample date:
1045 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1046
1047 Format  Output      Description
1048 ------  ----------  --------------------------------------------------------------
1049   d      10         Day of the month, 2 digits with leading zeros
1050   D      Wed        A textual representation of a day, three letters
1051   j      10         Day of the month without leading zeros
1052   l      Wednesday  A full textual representation of the day of the week
1053   S      th         English ordinal day of month suffix, 2 chars (use with j)
1054   w      3          Numeric representation of the day of the week
1055   z      9          The julian date, or day of the year (0-365)
1056   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1057   F      January    A full textual representation of the month
1058   m      01         Numeric representation of a month, with leading zeros
1059   M      Jan        Month name abbreviation, three letters
1060   n      1          Numeric representation of a month, without leading zeros
1061   t      31         Number of days in the given month
1062   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1063   Y      2007       A full numeric representation of a year, 4 digits
1064   y      07         A two digit representation of a year
1065   a      pm         Lowercase Ante meridiem and Post meridiem
1066   A      PM         Uppercase Ante meridiem and Post meridiem
1067   g      3          12-hour format of an hour without leading zeros
1068   G      15         24-hour format of an hour without leading zeros
1069   h      03         12-hour format of an hour with leading zeros
1070   H      15         24-hour format of an hour with leading zeros
1071   i      05         Minutes with leading zeros
1072   s      01         Seconds, with leading zeros
1073   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1074   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1075   T      CST        Timezone setting of the machine running the code
1076   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1077 </pre>
1078  *
1079  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1080  * <pre><code>
1081 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1082 document.write(dt.format('Y-m-d'));                         //2007-01-10
1083 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1084 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
1085  </code></pre>
1086  *
1087  * Here are some standard date/time patterns that you might find helpful.  They
1088  * are not part of the source of Date.js, but to use them you can simply copy this
1089  * block of code into any script that is included after Date.js and they will also become
1090  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1091  * <pre><code>
1092 Date.patterns = {
1093     ISO8601Long:"Y-m-d H:i:s",
1094     ISO8601Short:"Y-m-d",
1095     ShortDate: "n/j/Y",
1096     LongDate: "l, F d, Y",
1097     FullDateTime: "l, F d, Y g:i:s A",
1098     MonthDay: "F d",
1099     ShortTime: "g:i A",
1100     LongTime: "g:i:s A",
1101     SortableDateTime: "Y-m-d\\TH:i:s",
1102     UniversalSortableDateTime: "Y-m-d H:i:sO",
1103     YearMonth: "F, Y"
1104 };
1105 </code></pre>
1106  *
1107  * Example usage:
1108  * <pre><code>
1109 var dt = new Date();
1110 document.write(dt.format(Date.patterns.ShortDate));
1111  </code></pre>
1112  */
1113
1114 /*
1115  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1116  * They generate precompiled functions from date formats instead of parsing and
1117  * processing the pattern every time you format a date.  These functions are available
1118  * on every Date object (any javascript function).
1119  *
1120  * The original article and download are here:
1121  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1122  *
1123  */
1124  
1125  
1126  // was in core
1127 /**
1128  Returns the number of milliseconds between this date and date
1129  @param {Date} date (optional) Defaults to now
1130  @return {Number} The diff in milliseconds
1131  @member Date getElapsed
1132  */
1133 Date.prototype.getElapsed = function(date) {
1134         return Math.abs((date || new Date()).getTime()-this.getTime());
1135 };
1136 // was in date file..
1137
1138
1139 // private
1140 Date.parseFunctions = {count:0};
1141 // private
1142 Date.parseRegexes = [];
1143 // private
1144 Date.formatFunctions = {count:0};
1145
1146 // private
1147 Date.prototype.dateFormat = function(format) {
1148     if (Date.formatFunctions[format] == null) {
1149         Date.createNewFormat(format);
1150     }
1151     var func = Date.formatFunctions[format];
1152     return this[func]();
1153 };
1154
1155
1156 /**
1157  * Formats a date given the supplied format string
1158  * @param {String} format The format string
1159  * @return {String} The formatted date
1160  * @method
1161  */
1162 Date.prototype.format = Date.prototype.dateFormat;
1163
1164 // private
1165 Date.createNewFormat = function(format) {
1166     var funcName = "format" + Date.formatFunctions.count++;
1167     Date.formatFunctions[format] = funcName;
1168     var code = "Date.prototype." + funcName + " = function(){return ";
1169     var special = false;
1170     var ch = '';
1171     for (var i = 0; i < format.length; ++i) {
1172         ch = format.charAt(i);
1173         if (!special && ch == "\\") {
1174             special = true;
1175         }
1176         else if (special) {
1177             special = false;
1178             code += "'" + String.escape(ch) + "' + ";
1179         }
1180         else {
1181             code += Date.getFormatCode(ch);
1182         }
1183     }
1184     /** eval:var:zzzzzzzzzzzzz */
1185     eval(code.substring(0, code.length - 3) + ";}");
1186 };
1187
1188 // private
1189 Date.getFormatCode = function(character) {
1190     switch (character) {
1191     case "d":
1192         return "String.leftPad(this.getDate(), 2, '0') + ";
1193     case "D":
1194         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1195     case "j":
1196         return "this.getDate() + ";
1197     case "l":
1198         return "Date.dayNames[this.getDay()] + ";
1199     case "S":
1200         return "this.getSuffix() + ";
1201     case "w":
1202         return "this.getDay() + ";
1203     case "z":
1204         return "this.getDayOfYear() + ";
1205     case "W":
1206         return "this.getWeekOfYear() + ";
1207     case "F":
1208         return "Date.monthNames[this.getMonth()] + ";
1209     case "m":
1210         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1211     case "M":
1212         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1213     case "n":
1214         return "(this.getMonth() + 1) + ";
1215     case "t":
1216         return "this.getDaysInMonth() + ";
1217     case "L":
1218         return "(this.isLeapYear() ? 1 : 0) + ";
1219     case "Y":
1220         return "this.getFullYear() + ";
1221     case "y":
1222         return "('' + this.getFullYear()).substring(2, 4) + ";
1223     case "a":
1224         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1225     case "A":
1226         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1227     case "g":
1228         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1229     case "G":
1230         return "this.getHours() + ";
1231     case "h":
1232         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1233     case "H":
1234         return "String.leftPad(this.getHours(), 2, '0') + ";
1235     case "i":
1236         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1237     case "s":
1238         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1239     case "O":
1240         return "this.getGMTOffset() + ";
1241     case "P":
1242         return "this.getGMTColonOffset() + ";
1243     case "T":
1244         return "this.getTimezone() + ";
1245     case "Z":
1246         return "(this.getTimezoneOffset() * -60) + ";
1247     default:
1248         return "'" + String.escape(character) + "' + ";
1249     }
1250 };
1251
1252 /**
1253  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1254  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1255  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1256  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1257  * string or the parse operation will fail.
1258  * Example Usage:
1259 <pre><code>
1260 //dt = Fri May 25 2007 (current date)
1261 var dt = new Date();
1262
1263 //dt = Thu May 25 2006 (today's month/day in 2006)
1264 dt = Date.parseDate("2006", "Y");
1265
1266 //dt = Sun Jan 15 2006 (all date parts specified)
1267 dt = Date.parseDate("2006-1-15", "Y-m-d");
1268
1269 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1270 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1271 </code></pre>
1272  * @param {String} input The unparsed date as a string
1273  * @param {String} format The format the date is in
1274  * @return {Date} The parsed date
1275  * @static
1276  */
1277 Date.parseDate = function(input, format) {
1278     if (Date.parseFunctions[format] == null) {
1279         Date.createParser(format);
1280     }
1281     var func = Date.parseFunctions[format];
1282     return Date[func](input);
1283 };
1284 /**
1285  * @private
1286  */
1287
1288 Date.createParser = function(format) {
1289     var funcName = "parse" + Date.parseFunctions.count++;
1290     var regexNum = Date.parseRegexes.length;
1291     var currentGroup = 1;
1292     Date.parseFunctions[format] = funcName;
1293
1294     var code = "Date." + funcName + " = function(input){\n"
1295         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1296         + "var d = new Date();\n"
1297         + "y = d.getFullYear();\n"
1298         + "m = d.getMonth();\n"
1299         + "d = d.getDate();\n"
1300         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1301         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1302         + "if (results && results.length > 0) {";
1303     var regex = "";
1304
1305     var special = false;
1306     var ch = '';
1307     for (var i = 0; i < format.length; ++i) {
1308         ch = format.charAt(i);
1309         if (!special && ch == "\\") {
1310             special = true;
1311         }
1312         else if (special) {
1313             special = false;
1314             regex += String.escape(ch);
1315         }
1316         else {
1317             var obj = Date.formatCodeToRegex(ch, currentGroup);
1318             currentGroup += obj.g;
1319             regex += obj.s;
1320             if (obj.g && obj.c) {
1321                 code += obj.c;
1322             }
1323         }
1324     }
1325
1326     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1327         + "{v = new Date(y, m, d, h, i, s);}\n"
1328         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1329         + "{v = new Date(y, m, d, h, i);}\n"
1330         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1331         + "{v = new Date(y, m, d, h);}\n"
1332         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1333         + "{v = new Date(y, m, d);}\n"
1334         + "else if (y >= 0 && m >= 0)\n"
1335         + "{v = new Date(y, m);}\n"
1336         + "else if (y >= 0)\n"
1337         + "{v = new Date(y);}\n"
1338         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1339         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1340         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1341         + ";}";
1342
1343     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1344     /** eval:var:zzzzzzzzzzzzz */
1345     eval(code);
1346 };
1347
1348 // private
1349 Date.formatCodeToRegex = function(character, currentGroup) {
1350     switch (character) {
1351     case "D":
1352         return {g:0,
1353         c:null,
1354         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1355     case "j":
1356         return {g:1,
1357             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1358             s:"(\\d{1,2})"}; // day of month without leading zeroes
1359     case "d":
1360         return {g:1,
1361             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1362             s:"(\\d{2})"}; // day of month with leading zeroes
1363     case "l":
1364         return {g:0,
1365             c:null,
1366             s:"(?:" + Date.dayNames.join("|") + ")"};
1367     case "S":
1368         return {g:0,
1369             c:null,
1370             s:"(?:st|nd|rd|th)"};
1371     case "w":
1372         return {g:0,
1373             c:null,
1374             s:"\\d"};
1375     case "z":
1376         return {g:0,
1377             c:null,
1378             s:"(?:\\d{1,3})"};
1379     case "W":
1380         return {g:0,
1381             c:null,
1382             s:"(?:\\d{2})"};
1383     case "F":
1384         return {g:1,
1385             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1386             s:"(" + Date.monthNames.join("|") + ")"};
1387     case "M":
1388         return {g:1,
1389             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1390             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1391     case "n":
1392         return {g:1,
1393             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1394             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1395     case "m":
1396         return {g:1,
1397             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1398             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1399     case "t":
1400         return {g:0,
1401             c:null,
1402             s:"\\d{1,2}"};
1403     case "L":
1404         return {g:0,
1405             c:null,
1406             s:"(?:1|0)"};
1407     case "Y":
1408         return {g:1,
1409             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1410             s:"(\\d{4})"};
1411     case "y":
1412         return {g:1,
1413             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1414                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1415             s:"(\\d{1,2})"};
1416     case "a":
1417         return {g:1,
1418             c:"if (results[" + currentGroup + "] == 'am') {\n"
1419                 + "if (h == 12) { h = 0; }\n"
1420                 + "} else { if (h < 12) { h += 12; }}",
1421             s:"(am|pm)"};
1422     case "A":
1423         return {g:1,
1424             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1425                 + "if (h == 12) { h = 0; }\n"
1426                 + "} else { if (h < 12) { h += 12; }}",
1427             s:"(AM|PM)"};
1428     case "g":
1429     case "G":
1430         return {g:1,
1431             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1432             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1433     case "h":
1434     case "H":
1435         return {g:1,
1436             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1437             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1438     case "i":
1439         return {g:1,
1440             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1441             s:"(\\d{2})"};
1442     case "s":
1443         return {g:1,
1444             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1445             s:"(\\d{2})"};
1446     case "O":
1447         return {g:1,
1448             c:[
1449                 "o = results[", currentGroup, "];\n",
1450                 "var sn = o.substring(0,1);\n", // get + / - sign
1451                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1452                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1453                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1454                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1455             ].join(""),
1456             s:"([+\-]\\d{2,4})"};
1457     
1458     
1459     case "P":
1460         return {g:1,
1461                 c:[
1462                    "o = results[", currentGroup, "];\n",
1463                    "var sn = o.substring(0,1);\n",
1464                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1465                    "var mn = o.substring(4,6) % 60;\n",
1466                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1467                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1468             ].join(""),
1469             s:"([+\-]\\d{4})"};
1470     case "T":
1471         return {g:0,
1472             c:null,
1473             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1474     case "Z":
1475         return {g:1,
1476             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1477                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1478             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1479     default:
1480         return {g:0,
1481             c:null,
1482             s:String.escape(character)};
1483     }
1484 };
1485
1486 /**
1487  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1488  * @return {String} The abbreviated timezone name (e.g. 'CST')
1489  */
1490 Date.prototype.getTimezone = function() {
1491     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1492 };
1493
1494 /**
1495  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1496  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1497  */
1498 Date.prototype.getGMTOffset = function() {
1499     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1500         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1501         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1502 };
1503
1504 /**
1505  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1506  * @return {String} 2-characters representing hours and 2-characters representing minutes
1507  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1508  */
1509 Date.prototype.getGMTColonOffset = function() {
1510         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1511                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1512                 + ":"
1513                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1514 }
1515
1516 /**
1517  * Get the numeric day number of the year, adjusted for leap year.
1518  * @return {Number} 0 through 364 (365 in leap years)
1519  */
1520 Date.prototype.getDayOfYear = function() {
1521     var num = 0;
1522     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1523     for (var i = 0; i < this.getMonth(); ++i) {
1524         num += Date.daysInMonth[i];
1525     }
1526     return num + this.getDate() - 1;
1527 };
1528
1529 /**
1530  * Get the string representation of the numeric week number of the year
1531  * (equivalent to the format specifier 'W').
1532  * @return {String} '00' through '52'
1533  */
1534 Date.prototype.getWeekOfYear = function() {
1535     // Skip to Thursday of this week
1536     var now = this.getDayOfYear() + (4 - this.getDay());
1537     // Find the first Thursday of the year
1538     var jan1 = new Date(this.getFullYear(), 0, 1);
1539     var then = (7 - jan1.getDay() + 4);
1540     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1541 };
1542
1543 /**
1544  * Whether or not the current date is in a leap year.
1545  * @return {Boolean} True if the current date is in a leap year, else false
1546  */
1547 Date.prototype.isLeapYear = function() {
1548     var year = this.getFullYear();
1549     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1550 };
1551
1552 /**
1553  * Get the first day of the current month, adjusted for leap year.  The returned value
1554  * is the numeric day index within the week (0-6) which can be used in conjunction with
1555  * the {@link #monthNames} array to retrieve the textual day name.
1556  * Example:
1557  *<pre><code>
1558 var dt = new Date('1/10/2007');
1559 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1560 </code></pre>
1561  * @return {Number} The day number (0-6)
1562  */
1563 Date.prototype.getFirstDayOfMonth = function() {
1564     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1565     return (day < 0) ? (day + 7) : day;
1566 };
1567
1568 /**
1569  * Get the last day of the current month, adjusted for leap year.  The returned value
1570  * is the numeric day index within the week (0-6) which can be used in conjunction with
1571  * the {@link #monthNames} array to retrieve the textual day name.
1572  * Example:
1573  *<pre><code>
1574 var dt = new Date('1/10/2007');
1575 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1576 </code></pre>
1577  * @return {Number} The day number (0-6)
1578  */
1579 Date.prototype.getLastDayOfMonth = function() {
1580     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1581     return (day < 0) ? (day + 7) : day;
1582 };
1583
1584
1585 /**
1586  * Get the first date of this date's month
1587  * @return {Date}
1588  */
1589 Date.prototype.getFirstDateOfMonth = function() {
1590     return new Date(this.getFullYear(), this.getMonth(), 1);
1591 };
1592
1593 /**
1594  * Get the last date of this date's month
1595  * @return {Date}
1596  */
1597 Date.prototype.getLastDateOfMonth = function() {
1598     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1599 };
1600 /**
1601  * Get the number of days in the current month, adjusted for leap year.
1602  * @return {Number} The number of days in the month
1603  */
1604 Date.prototype.getDaysInMonth = function() {
1605     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1606     return Date.daysInMonth[this.getMonth()];
1607 };
1608
1609 /**
1610  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1611  * @return {String} 'st, 'nd', 'rd' or 'th'
1612  */
1613 Date.prototype.getSuffix = function() {
1614     switch (this.getDate()) {
1615         case 1:
1616         case 21:
1617         case 31:
1618             return "st";
1619         case 2:
1620         case 22:
1621             return "nd";
1622         case 3:
1623         case 23:
1624             return "rd";
1625         default:
1626             return "th";
1627     }
1628 };
1629
1630 // private
1631 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1632
1633 /**
1634  * An array of textual month names.
1635  * Override these values for international dates, for example...
1636  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1637  * @type Array
1638  * @static
1639  */
1640 Date.monthNames =
1641    ["January",
1642     "February",
1643     "March",
1644     "April",
1645     "May",
1646     "June",
1647     "July",
1648     "August",
1649     "September",
1650     "October",
1651     "November",
1652     "December"];
1653
1654 /**
1655  * An array of textual day names.
1656  * Override these values for international dates, for example...
1657  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1658  * @type Array
1659  * @static
1660  */
1661 Date.dayNames =
1662    ["Sunday",
1663     "Monday",
1664     "Tuesday",
1665     "Wednesday",
1666     "Thursday",
1667     "Friday",
1668     "Saturday"];
1669
1670 // private
1671 Date.y2kYear = 50;
1672 // private
1673 Date.monthNumbers = {
1674     Jan:0,
1675     Feb:1,
1676     Mar:2,
1677     Apr:3,
1678     May:4,
1679     Jun:5,
1680     Jul:6,
1681     Aug:7,
1682     Sep:8,
1683     Oct:9,
1684     Nov:10,
1685     Dec:11};
1686
1687 /**
1688  * Creates and returns a new Date instance with the exact same date value as the called instance.
1689  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1690  * variable will also be changed.  When the intention is to create a new variable that will not
1691  * modify the original instance, you should create a clone.
1692  *
1693  * Example of correctly cloning a date:
1694  * <pre><code>
1695 //wrong way:
1696 var orig = new Date('10/1/2006');
1697 var copy = orig;
1698 copy.setDate(5);
1699 document.write(orig);  //returns 'Thu Oct 05 2006'!
1700
1701 //correct way:
1702 var orig = new Date('10/1/2006');
1703 var copy = orig.clone();
1704 copy.setDate(5);
1705 document.write(orig);  //returns 'Thu Oct 01 2006'
1706 </code></pre>
1707  * @return {Date} The new Date instance
1708  */
1709 Date.prototype.clone = function() {
1710         return new Date(this.getTime());
1711 };
1712
1713 /**
1714  * Clears any time information from this date
1715  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1716  @return {Date} this or the clone
1717  */
1718 Date.prototype.clearTime = function(clone){
1719     if(clone){
1720         return this.clone().clearTime();
1721     }
1722     this.setHours(0);
1723     this.setMinutes(0);
1724     this.setSeconds(0);
1725     this.setMilliseconds(0);
1726     return this;
1727 };
1728
1729 // private
1730 // safari setMonth is broken -- check that this is only donw once...
1731 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1732     Date.brokenSetMonth = Date.prototype.setMonth;
1733         Date.prototype.setMonth = function(num){
1734                 if(num <= -1){
1735                         var n = Math.ceil(-num);
1736                         var back_year = Math.ceil(n/12);
1737                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1738                         this.setFullYear(this.getFullYear() - back_year);
1739                         return Date.brokenSetMonth.call(this, month);
1740                 } else {
1741                         return Date.brokenSetMonth.apply(this, arguments);
1742                 }
1743         };
1744 }
1745
1746 /** Date interval constant 
1747 * @static 
1748 * @type String */
1749 Date.MILLI = "ms";
1750 /** Date interval constant 
1751 * @static 
1752 * @type String */
1753 Date.SECOND = "s";
1754 /** Date interval constant 
1755 * @static 
1756 * @type String */
1757 Date.MINUTE = "mi";
1758 /** Date interval constant 
1759 * @static 
1760 * @type String */
1761 Date.HOUR = "h";
1762 /** Date interval constant 
1763 * @static 
1764 * @type String */
1765 Date.DAY = "d";
1766 /** Date interval constant 
1767 * @static 
1768 * @type String */
1769 Date.MONTH = "mo";
1770 /** Date interval constant 
1771 * @static 
1772 * @type String */
1773 Date.YEAR = "y";
1774
1775 /**
1776  * Provides a convenient method of performing basic date arithmetic.  This method
1777  * does not modify the Date instance being called - it creates and returns
1778  * a new Date instance containing the resulting date value.
1779  *
1780  * Examples:
1781  * <pre><code>
1782 //Basic usage:
1783 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1784 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1785
1786 //Negative values will subtract correctly:
1787 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1788 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1789
1790 //You can even chain several calls together in one line!
1791 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1792 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1793  </code></pre>
1794  *
1795  * @param {String} interval   A valid date interval enum value
1796  * @param {Number} value      The amount to add to the current date
1797  * @return {Date} The new Date instance
1798  */
1799 Date.prototype.add = function(interval, value){
1800   var d = this.clone();
1801   if (!interval || value === 0) { return d; }
1802   switch(interval.toLowerCase()){
1803     case Date.MILLI:
1804       d.setMilliseconds(this.getMilliseconds() + value);
1805       break;
1806     case Date.SECOND:
1807       d.setSeconds(this.getSeconds() + value);
1808       break;
1809     case Date.MINUTE:
1810       d.setMinutes(this.getMinutes() + value);
1811       break;
1812     case Date.HOUR:
1813       d.setHours(this.getHours() + value);
1814       break;
1815     case Date.DAY:
1816       d.setDate(this.getDate() + value);
1817       break;
1818     case Date.MONTH:
1819       var day = this.getDate();
1820       if(day > 28){
1821           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1822       }
1823       d.setDate(day);
1824       d.setMonth(this.getMonth() + value);
1825       break;
1826     case Date.YEAR:
1827       d.setFullYear(this.getFullYear() + value);
1828       break;
1829   }
1830   return d;
1831 };
1832 /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842
1843 /**
1844  * @class Roo.lib.Dom
1845  * @static
1846  * 
1847  * Dom utils (from YIU afaik)
1848  * 
1849  **/
1850 Roo.lib.Dom = {
1851     /**
1852      * Get the view width
1853      * @param {Boolean} full True will get the full document, otherwise it's the view width
1854      * @return {Number} The width
1855      */
1856      
1857     getViewWidth : function(full) {
1858         return full ? this.getDocumentWidth() : this.getViewportWidth();
1859     },
1860     /**
1861      * Get the view height
1862      * @param {Boolean} full True will get the full document, otherwise it's the view height
1863      * @return {Number} The height
1864      */
1865     getViewHeight : function(full) {
1866         return full ? this.getDocumentHeight() : this.getViewportHeight();
1867     },
1868
1869     getDocumentHeight: function() {
1870         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1871         return Math.max(scrollHeight, this.getViewportHeight());
1872     },
1873
1874     getDocumentWidth: function() {
1875         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1876         return Math.max(scrollWidth, this.getViewportWidth());
1877     },
1878
1879     getViewportHeight: function() {
1880         var height = self.innerHeight;
1881         var mode = document.compatMode;
1882
1883         if ((mode || Roo.isIE) && !Roo.isOpera) {
1884             height = (mode == "CSS1Compat") ?
1885                      document.documentElement.clientHeight :
1886                      document.body.clientHeight;
1887         }
1888
1889         return height;
1890     },
1891
1892     getViewportWidth: function() {
1893         var width = self.innerWidth;
1894         var mode = document.compatMode;
1895
1896         if (mode || Roo.isIE) {
1897             width = (mode == "CSS1Compat") ?
1898                     document.documentElement.clientWidth :
1899                     document.body.clientWidth;
1900         }
1901         return width;
1902     },
1903
1904     isAncestor : function(p, c) {
1905         p = Roo.getDom(p);
1906         c = Roo.getDom(c);
1907         if (!p || !c) {
1908             return false;
1909         }
1910
1911         if (p.contains && !Roo.isSafari) {
1912             return p.contains(c);
1913         } else if (p.compareDocumentPosition) {
1914             return !!(p.compareDocumentPosition(c) & 16);
1915         } else {
1916             var parent = c.parentNode;
1917             while (parent) {
1918                 if (parent == p) {
1919                     return true;
1920                 }
1921                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1922                     return false;
1923                 }
1924                 parent = parent.parentNode;
1925             }
1926             return false;
1927         }
1928     },
1929
1930     getRegion : function(el) {
1931         return Roo.lib.Region.getRegion(el);
1932     },
1933
1934     getY : function(el) {
1935         return this.getXY(el)[1];
1936     },
1937
1938     getX : function(el) {
1939         return this.getXY(el)[0];
1940     },
1941
1942     getXY : function(el) {
1943         var p, pe, b, scroll, bd = document.body;
1944         el = Roo.getDom(el);
1945         var fly = Roo.lib.AnimBase.fly;
1946         if (el.getBoundingClientRect) {
1947             b = el.getBoundingClientRect();
1948             scroll = fly(document).getScroll();
1949             return [b.left + scroll.left, b.top + scroll.top];
1950         }
1951         var x = 0, y = 0;
1952
1953         p = el;
1954
1955         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1956
1957         while (p) {
1958
1959             x += p.offsetLeft;
1960             y += p.offsetTop;
1961
1962             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1963                 hasAbsolute = true;
1964             }
1965
1966             if (Roo.isGecko) {
1967                 pe = fly(p);
1968
1969                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1970                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1971
1972
1973                 x += bl;
1974                 y += bt;
1975
1976
1977                 if (p != el && pe.getStyle('overflow') != 'visible') {
1978                     x += bl;
1979                     y += bt;
1980                 }
1981             }
1982             p = p.offsetParent;
1983         }
1984
1985         if (Roo.isSafari && hasAbsolute) {
1986             x -= bd.offsetLeft;
1987             y -= bd.offsetTop;
1988         }
1989
1990         if (Roo.isGecko && !hasAbsolute) {
1991             var dbd = fly(bd);
1992             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1993             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1994         }
1995
1996         p = el.parentNode;
1997         while (p && p != bd) {
1998             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1999                 x -= p.scrollLeft;
2000                 y -= p.scrollTop;
2001             }
2002             p = p.parentNode;
2003         }
2004         return [x, y];
2005     },
2006  
2007   
2008
2009
2010     setXY : function(el, xy) {
2011         el = Roo.fly(el, '_setXY');
2012         el.position();
2013         var pts = el.translatePoints(xy);
2014         if (xy[0] !== false) {
2015             el.dom.style.left = pts.left + "px";
2016         }
2017         if (xy[1] !== false) {
2018             el.dom.style.top = pts.top + "px";
2019         }
2020     },
2021
2022     setX : function(el, x) {
2023         this.setXY(el, [x, false]);
2024     },
2025
2026     setY : function(el, y) {
2027         this.setXY(el, [false, y]);
2028     }
2029 };
2030 /*
2031  * Portions of this file are based on pieces of Yahoo User Interface Library
2032  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2033  * YUI licensed under the BSD License:
2034  * http://developer.yahoo.net/yui/license.txt
2035  * <script type="text/javascript">
2036  *
2037  */
2038
2039 Roo.lib.Event = function() {
2040     var loadComplete = false;
2041     var listeners = [];
2042     var unloadListeners = [];
2043     var retryCount = 0;
2044     var onAvailStack = [];
2045     var counter = 0;
2046     var lastError = null;
2047
2048     return {
2049         POLL_RETRYS: 200,
2050         POLL_INTERVAL: 20,
2051         EL: 0,
2052         TYPE: 1,
2053         FN: 2,
2054         WFN: 3,
2055         OBJ: 3,
2056         ADJ_SCOPE: 4,
2057         _interval: null,
2058
2059         startInterval: function() {
2060             if (!this._interval) {
2061                 var self = this;
2062                 var callback = function() {
2063                     self._tryPreloadAttach();
2064                 };
2065                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2066
2067             }
2068         },
2069
2070         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2071             onAvailStack.push({ id:         p_id,
2072                 fn:         p_fn,
2073                 obj:        p_obj,
2074                 override:   p_override,
2075                 checkReady: false    });
2076
2077             retryCount = this.POLL_RETRYS;
2078             this.startInterval();
2079         },
2080
2081
2082         addListener: function(el, eventName, fn) {
2083             el = Roo.getDom(el);
2084             if (!el || !fn) {
2085                 return false;
2086             }
2087
2088             if ("unload" == eventName) {
2089                 unloadListeners[unloadListeners.length] =
2090                 [el, eventName, fn];
2091                 return true;
2092             }
2093
2094             var wrappedFn = function(e) {
2095                 return fn(Roo.lib.Event.getEvent(e));
2096             };
2097
2098             var li = [el, eventName, fn, wrappedFn];
2099
2100             var index = listeners.length;
2101             listeners[index] = li;
2102
2103             this.doAdd(el, eventName, wrappedFn, false);
2104             return true;
2105
2106         },
2107
2108
2109         removeListener: function(el, eventName, fn) {
2110             var i, len;
2111
2112             el = Roo.getDom(el);
2113
2114             if(!fn) {
2115                 return this.purgeElement(el, false, eventName);
2116             }
2117
2118
2119             if ("unload" == eventName) {
2120
2121                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2122                     var li = unloadListeners[i];
2123                     if (li &&
2124                         li[0] == el &&
2125                         li[1] == eventName &&
2126                         li[2] == fn) {
2127                         unloadListeners.splice(i, 1);
2128                         return true;
2129                     }
2130                 }
2131
2132                 return false;
2133             }
2134
2135             var cacheItem = null;
2136
2137
2138             var index = arguments[3];
2139
2140             if ("undefined" == typeof index) {
2141                 index = this._getCacheIndex(el, eventName, fn);
2142             }
2143
2144             if (index >= 0) {
2145                 cacheItem = listeners[index];
2146             }
2147
2148             if (!el || !cacheItem) {
2149                 return false;
2150             }
2151
2152             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2153
2154             delete listeners[index][this.WFN];
2155             delete listeners[index][this.FN];
2156             listeners.splice(index, 1);
2157
2158             return true;
2159
2160         },
2161
2162
2163         getTarget: function(ev, resolveTextNode) {
2164             ev = ev.browserEvent || ev;
2165             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2166             var t = ev.target || ev.srcElement;
2167             return this.resolveTextNode(t);
2168         },
2169
2170
2171         resolveTextNode: function(node) {
2172             if (Roo.isSafari && node && 3 == node.nodeType) {
2173                 return node.parentNode;
2174             } else {
2175                 return node;
2176             }
2177         },
2178
2179
2180         getPageX: function(ev) {
2181             ev = ev.browserEvent || ev;
2182             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2183             var x = ev.pageX;
2184             if (!x && 0 !== x) {
2185                 x = ev.clientX || 0;
2186
2187                 if (Roo.isIE) {
2188                     x += this.getScroll()[1];
2189                 }
2190             }
2191
2192             return x;
2193         },
2194
2195
2196         getPageY: function(ev) {
2197             ev = ev.browserEvent || ev;
2198             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2199             var y = ev.pageY;
2200             if (!y && 0 !== y) {
2201                 y = ev.clientY || 0;
2202
2203                 if (Roo.isIE) {
2204                     y += this.getScroll()[0];
2205                 }
2206             }
2207
2208
2209             return y;
2210         },
2211
2212
2213         getXY: function(ev) {
2214             ev = ev.browserEvent || ev;
2215             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2216             return [this.getPageX(ev), this.getPageY(ev)];
2217         },
2218
2219
2220         getRelatedTarget: function(ev) {
2221             ev = ev.browserEvent || ev;
2222             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2223             var t = ev.relatedTarget;
2224             if (!t) {
2225                 if (ev.type == "mouseout") {
2226                     t = ev.toElement;
2227                 } else if (ev.type == "mouseover") {
2228                     t = ev.fromElement;
2229                 }
2230             }
2231
2232             return this.resolveTextNode(t);
2233         },
2234
2235
2236         getTime: function(ev) {
2237             ev = ev.browserEvent || ev;
2238             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2239             if (!ev.time) {
2240                 var t = new Date().getTime();
2241                 try {
2242                     ev.time = t;
2243                 } catch(ex) {
2244                     this.lastError = ex;
2245                     return t;
2246                 }
2247             }
2248
2249             return ev.time;
2250         },
2251
2252
2253         stopEvent: function(ev) {
2254             this.stopPropagation(ev);
2255             this.preventDefault(ev);
2256         },
2257
2258
2259         stopPropagation: function(ev) {
2260             ev = ev.browserEvent || ev;
2261             if (ev.stopPropagation) {
2262                 ev.stopPropagation();
2263             } else {
2264                 ev.cancelBubble = true;
2265             }
2266         },
2267
2268
2269         preventDefault: function(ev) {
2270             ev = ev.browserEvent || ev;
2271             if(ev.preventDefault) {
2272                 ev.preventDefault();
2273             } else {
2274                 ev.returnValue = false;
2275             }
2276         },
2277
2278
2279         getEvent: function(e) {
2280             var ev = e || window.event;
2281             if (!ev) {
2282                 var c = this.getEvent.caller;
2283                 while (c) {
2284                     ev = c.arguments[0];
2285                     if (ev && Event == ev.constructor) {
2286                         break;
2287                     }
2288                     c = c.caller;
2289                 }
2290             }
2291             return ev;
2292         },
2293
2294
2295         getCharCode: function(ev) {
2296             ev = ev.browserEvent || ev;
2297             return ev.charCode || ev.keyCode || 0;
2298         },
2299
2300
2301         _getCacheIndex: function(el, eventName, fn) {
2302             for (var i = 0,len = listeners.length; i < len; ++i) {
2303                 var li = listeners[i];
2304                 if (li &&
2305                     li[this.FN] == fn &&
2306                     li[this.EL] == el &&
2307                     li[this.TYPE] == eventName) {
2308                     return i;
2309                 }
2310             }
2311
2312             return -1;
2313         },
2314
2315
2316         elCache: {},
2317
2318
2319         getEl: function(id) {
2320             return document.getElementById(id);
2321         },
2322
2323
2324         clearCache: function() {
2325         },
2326
2327
2328         _load: function(e) {
2329             loadComplete = true;
2330             var EU = Roo.lib.Event;
2331
2332
2333             if (Roo.isIE) {
2334                 EU.doRemove(window, "load", EU._load);
2335             }
2336         },
2337
2338
2339         _tryPreloadAttach: function() {
2340
2341             if (this.locked) {
2342                 return false;
2343             }
2344
2345             this.locked = true;
2346
2347
2348             var tryAgain = !loadComplete;
2349             if (!tryAgain) {
2350                 tryAgain = (retryCount > 0);
2351             }
2352
2353
2354             var notAvail = [];
2355             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2356                 var item = onAvailStack[i];
2357                 if (item) {
2358                     var el = this.getEl(item.id);
2359
2360                     if (el) {
2361                         if (!item.checkReady ||
2362                             loadComplete ||
2363                             el.nextSibling ||
2364                             (document && document.body)) {
2365
2366                             var scope = el;
2367                             if (item.override) {
2368                                 if (item.override === true) {
2369                                     scope = item.obj;
2370                                 } else {
2371                                     scope = item.override;
2372                                 }
2373                             }
2374                             item.fn.call(scope, item.obj);
2375                             onAvailStack[i] = null;
2376                         }
2377                     } else {
2378                         notAvail.push(item);
2379                     }
2380                 }
2381             }
2382
2383             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2384
2385             if (tryAgain) {
2386
2387                 this.startInterval();
2388             } else {
2389                 clearInterval(this._interval);
2390                 this._interval = null;
2391             }
2392
2393             this.locked = false;
2394
2395             return true;
2396
2397         },
2398
2399
2400         purgeElement: function(el, recurse, eventName) {
2401             var elListeners = this.getListeners(el, eventName);
2402             if (elListeners) {
2403                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2404                     var l = elListeners[i];
2405                     this.removeListener(el, l.type, l.fn);
2406                 }
2407             }
2408
2409             if (recurse && el && el.childNodes) {
2410                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2411                     this.purgeElement(el.childNodes[i], recurse, eventName);
2412                 }
2413             }
2414         },
2415
2416
2417         getListeners: function(el, eventName) {
2418             var results = [], searchLists;
2419             if (!eventName) {
2420                 searchLists = [listeners, unloadListeners];
2421             } else if (eventName == "unload") {
2422                 searchLists = [unloadListeners];
2423             } else {
2424                 searchLists = [listeners];
2425             }
2426
2427             for (var j = 0; j < searchLists.length; ++j) {
2428                 var searchList = searchLists[j];
2429                 if (searchList && searchList.length > 0) {
2430                     for (var i = 0,len = searchList.length; i < len; ++i) {
2431                         var l = searchList[i];
2432                         if (l && l[this.EL] === el &&
2433                             (!eventName || eventName === l[this.TYPE])) {
2434                             results.push({
2435                                 type:   l[this.TYPE],
2436                                 fn:     l[this.FN],
2437                                 obj:    l[this.OBJ],
2438                                 adjust: l[this.ADJ_SCOPE],
2439                                 index:  i
2440                             });
2441                         }
2442                     }
2443                 }
2444             }
2445
2446             return (results.length) ? results : null;
2447         },
2448
2449
2450         _unload: function(e) {
2451
2452             var EU = Roo.lib.Event, i, j, l, len, index;
2453
2454             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2455                 l = unloadListeners[i];
2456                 if (l) {
2457                     var scope = window;
2458                     if (l[EU.ADJ_SCOPE]) {
2459                         if (l[EU.ADJ_SCOPE] === true) {
2460                             scope = l[EU.OBJ];
2461                         } else {
2462                             scope = l[EU.ADJ_SCOPE];
2463                         }
2464                     }
2465                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2466                     unloadListeners[i] = null;
2467                     l = null;
2468                     scope = null;
2469                 }
2470             }
2471
2472             unloadListeners = null;
2473
2474             if (listeners && listeners.length > 0) {
2475                 j = listeners.length;
2476                 while (j) {
2477                     index = j - 1;
2478                     l = listeners[index];
2479                     if (l) {
2480                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2481                                 l[EU.FN], index);
2482                     }
2483                     j = j - 1;
2484                 }
2485                 l = null;
2486
2487                 EU.clearCache();
2488             }
2489
2490             EU.doRemove(window, "unload", EU._unload);
2491
2492         },
2493
2494
2495         getScroll: function() {
2496             var dd = document.documentElement, db = document.body;
2497             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2498                 return [dd.scrollTop, dd.scrollLeft];
2499             } else if (db) {
2500                 return [db.scrollTop, db.scrollLeft];
2501             } else {
2502                 return [0, 0];
2503             }
2504         },
2505
2506
2507         doAdd: function () {
2508             if (window.addEventListener) {
2509                 return function(el, eventName, fn, capture) {
2510                     el.addEventListener(eventName, fn, (capture));
2511                 };
2512             } else if (window.attachEvent) {
2513                 return function(el, eventName, fn, capture) {
2514                     el.attachEvent("on" + eventName, fn);
2515                 };
2516             } else {
2517                 return function() {
2518                 };
2519             }
2520         }(),
2521
2522
2523         doRemove: function() {
2524             if (window.removeEventListener) {
2525                 return function (el, eventName, fn, capture) {
2526                     el.removeEventListener(eventName, fn, (capture));
2527                 };
2528             } else if (window.detachEvent) {
2529                 return function (el, eventName, fn) {
2530                     el.detachEvent("on" + eventName, fn);
2531                 };
2532             } else {
2533                 return function() {
2534                 };
2535             }
2536         }()
2537     };
2538     
2539 }();
2540 (function() {     
2541    
2542     var E = Roo.lib.Event;
2543     E.on = E.addListener;
2544     E.un = E.removeListener;
2545
2546     if (document && document.body) {
2547         E._load();
2548     } else {
2549         E.doAdd(window, "load", E._load);
2550     }
2551     E.doAdd(window, "unload", E._unload);
2552     E._tryPreloadAttach();
2553 })();
2554
2555 /*
2556  * Portions of this file are based on pieces of Yahoo User Interface Library
2557  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2558  * YUI licensed under the BSD License:
2559  * http://developer.yahoo.net/yui/license.txt
2560  * <script type="text/javascript">
2561  *
2562  */
2563
2564 (function() {
2565     /**
2566      * @class Roo.lib.Ajax
2567      *
2568      */
2569     Roo.lib.Ajax = {
2570         /**
2571          * @static 
2572          */
2573         request : function(method, uri, cb, data, options) {
2574             if(options){
2575                 var hs = options.headers;
2576                 if(hs){
2577                     for(var h in hs){
2578                         if(hs.hasOwnProperty(h)){
2579                             this.initHeader(h, hs[h], false);
2580                         }
2581                     }
2582                 }
2583                 if(options.xmlData){
2584                     this.initHeader('Content-Type', 'text/xml', false);
2585                     method = 'POST';
2586                     data = options.xmlData;
2587                 }
2588             }
2589
2590             return this.asyncRequest(method, uri, cb, data);
2591         },
2592
2593         serializeForm : function(form) {
2594             if(typeof form == 'string') {
2595                 form = (document.getElementById(form) || document.forms[form]);
2596             }
2597
2598             var el, name, val, disabled, data = '', hasSubmit = false;
2599             for (var i = 0; i < form.elements.length; i++) {
2600                 el = form.elements[i];
2601                 disabled = form.elements[i].disabled;
2602                 name = form.elements[i].name;
2603                 val = form.elements[i].value;
2604
2605                 if (!disabled && name){
2606                     switch (el.type)
2607                             {
2608                         case 'select-one':
2609                         case 'select-multiple':
2610                             for (var j = 0; j < el.options.length; j++) {
2611                                 if (el.options[j].selected) {
2612                                     if (Roo.isIE) {
2613                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2614                                     }
2615                                     else {
2616                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2617                                     }
2618                                 }
2619                             }
2620                             break;
2621                         case 'radio':
2622                         case 'checkbox':
2623                             if (el.checked) {
2624                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2625                             }
2626                             break;
2627                         case 'file':
2628
2629                         case undefined:
2630
2631                         case 'reset':
2632
2633                         case 'button':
2634
2635                             break;
2636                         case 'submit':
2637                             if(hasSubmit == false) {
2638                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2639                                 hasSubmit = true;
2640                             }
2641                             break;
2642                         default:
2643                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2644                             break;
2645                     }
2646                 }
2647             }
2648             data = data.substr(0, data.length - 1);
2649             return data;
2650         },
2651
2652         headers:{},
2653
2654         hasHeaders:false,
2655
2656         useDefaultHeader:true,
2657
2658         defaultPostHeader:'application/x-www-form-urlencoded',
2659
2660         useDefaultXhrHeader:true,
2661
2662         defaultXhrHeader:'XMLHttpRequest',
2663
2664         hasDefaultHeaders:true,
2665
2666         defaultHeaders:{},
2667
2668         poll:{},
2669
2670         timeout:{},
2671
2672         pollInterval:50,
2673
2674         transactionId:0,
2675
2676         setProgId:function(id)
2677         {
2678             this.activeX.unshift(id);
2679         },
2680
2681         setDefaultPostHeader:function(b)
2682         {
2683             this.useDefaultHeader = b;
2684         },
2685
2686         setDefaultXhrHeader:function(b)
2687         {
2688             this.useDefaultXhrHeader = b;
2689         },
2690
2691         setPollingInterval:function(i)
2692         {
2693             if (typeof i == 'number' && isFinite(i)) {
2694                 this.pollInterval = i;
2695             }
2696         },
2697
2698         createXhrObject:function(transactionId)
2699         {
2700             var obj,http;
2701             try
2702             {
2703
2704                 http = new XMLHttpRequest();
2705
2706                 obj = { conn:http, tId:transactionId };
2707             }
2708             catch(e)
2709             {
2710                 for (var i = 0; i < this.activeX.length; ++i) {
2711                     try
2712                     {
2713
2714                         http = new ActiveXObject(this.activeX[i]);
2715
2716                         obj = { conn:http, tId:transactionId };
2717                         break;
2718                     }
2719                     catch(e) {
2720                     }
2721                 }
2722             }
2723             finally
2724             {
2725                 return obj;
2726             }
2727         },
2728
2729         getConnectionObject:function()
2730         {
2731             var o;
2732             var tId = this.transactionId;
2733
2734             try
2735             {
2736                 o = this.createXhrObject(tId);
2737                 if (o) {
2738                     this.transactionId++;
2739                 }
2740             }
2741             catch(e) {
2742             }
2743             finally
2744             {
2745                 return o;
2746             }
2747         },
2748
2749         asyncRequest:function(method, uri, callback, postData)
2750         {
2751             var o = this.getConnectionObject();
2752
2753             if (!o) {
2754                 return null;
2755             }
2756             else {
2757                 o.conn.open(method, uri, true);
2758
2759                 if (this.useDefaultXhrHeader) {
2760                     if (!this.defaultHeaders['X-Requested-With']) {
2761                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2762                     }
2763                 }
2764
2765                 if(postData && this.useDefaultHeader){
2766                     this.initHeader('Content-Type', this.defaultPostHeader);
2767                 }
2768
2769                  if (this.hasDefaultHeaders || this.hasHeaders) {
2770                     this.setHeader(o);
2771                 }
2772
2773                 this.handleReadyState(o, callback);
2774                 o.conn.send(postData || null);
2775
2776                 return o;
2777             }
2778         },
2779
2780         handleReadyState:function(o, callback)
2781         {
2782             var oConn = this;
2783
2784             if (callback && callback.timeout) {
2785                 
2786                 this.timeout[o.tId] = window.setTimeout(function() {
2787                     oConn.abort(o, callback, true);
2788                 }, callback.timeout);
2789             }
2790
2791             this.poll[o.tId] = window.setInterval(
2792                     function() {
2793                         if (o.conn && o.conn.readyState == 4) {
2794                             window.clearInterval(oConn.poll[o.tId]);
2795                             delete oConn.poll[o.tId];
2796
2797                             if(callback && callback.timeout) {
2798                                 window.clearTimeout(oConn.timeout[o.tId]);
2799                                 delete oConn.timeout[o.tId];
2800                             }
2801
2802                             oConn.handleTransactionResponse(o, callback);
2803                         }
2804                     }
2805                     , this.pollInterval);
2806         },
2807
2808         handleTransactionResponse:function(o, callback, isAbort)
2809         {
2810
2811             if (!callback) {
2812                 this.releaseObject(o);
2813                 return;
2814             }
2815
2816             var httpStatus, responseObject;
2817
2818             try
2819             {
2820                 if (o.conn.status !== undefined && o.conn.status != 0) {
2821                     httpStatus = o.conn.status;
2822                 }
2823                 else {
2824                     httpStatus = 13030;
2825                 }
2826             }
2827             catch(e) {
2828
2829
2830                 httpStatus = 13030;
2831             }
2832
2833             if (httpStatus >= 200 && httpStatus < 300) {
2834                 responseObject = this.createResponseObject(o, callback.argument);
2835                 if (callback.success) {
2836                     if (!callback.scope) {
2837                         callback.success(responseObject);
2838                     }
2839                     else {
2840
2841
2842                         callback.success.apply(callback.scope, [responseObject]);
2843                     }
2844                 }
2845             }
2846             else {
2847                 switch (httpStatus) {
2848
2849                     case 12002:
2850                     case 12029:
2851                     case 12030:
2852                     case 12031:
2853                     case 12152:
2854                     case 13030:
2855                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2856                         if (callback.failure) {
2857                             if (!callback.scope) {
2858                                 callback.failure(responseObject);
2859                             }
2860                             else {
2861                                 callback.failure.apply(callback.scope, [responseObject]);
2862                             }
2863                         }
2864                         break;
2865                     default:
2866                         responseObject = this.createResponseObject(o, callback.argument);
2867                         if (callback.failure) {
2868                             if (!callback.scope) {
2869                                 callback.failure(responseObject);
2870                             }
2871                             else {
2872                                 callback.failure.apply(callback.scope, [responseObject]);
2873                             }
2874                         }
2875                 }
2876             }
2877
2878             this.releaseObject(o);
2879             responseObject = null;
2880         },
2881
2882         createResponseObject:function(o, callbackArg)
2883         {
2884             var obj = {};
2885             var headerObj = {};
2886
2887             try
2888             {
2889                 var headerStr = o.conn.getAllResponseHeaders();
2890                 var header = headerStr.split('\n');
2891                 for (var i = 0; i < header.length; i++) {
2892                     var delimitPos = header[i].indexOf(':');
2893                     if (delimitPos != -1) {
2894                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2895                     }
2896                 }
2897             }
2898             catch(e) {
2899             }
2900
2901             obj.tId = o.tId;
2902             obj.status = o.conn.status;
2903             obj.statusText = o.conn.statusText;
2904             obj.getResponseHeader = headerObj;
2905             obj.getAllResponseHeaders = headerStr;
2906             obj.responseText = o.conn.responseText;
2907             obj.responseXML = o.conn.responseXML;
2908
2909             if (typeof callbackArg !== undefined) {
2910                 obj.argument = callbackArg;
2911             }
2912
2913             return obj;
2914         },
2915
2916         createExceptionObject:function(tId, callbackArg, isAbort)
2917         {
2918             var COMM_CODE = 0;
2919             var COMM_ERROR = 'communication failure';
2920             var ABORT_CODE = -1;
2921             var ABORT_ERROR = 'transaction aborted';
2922
2923             var obj = {};
2924
2925             obj.tId = tId;
2926             if (isAbort) {
2927                 obj.status = ABORT_CODE;
2928                 obj.statusText = ABORT_ERROR;
2929             }
2930             else {
2931                 obj.status = COMM_CODE;
2932                 obj.statusText = COMM_ERROR;
2933             }
2934
2935             if (callbackArg) {
2936                 obj.argument = callbackArg;
2937             }
2938
2939             return obj;
2940         },
2941
2942         initHeader:function(label, value, isDefault)
2943         {
2944             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2945
2946             if (headerObj[label] === undefined) {
2947                 headerObj[label] = value;
2948             }
2949             else {
2950
2951
2952                 headerObj[label] = value + "," + headerObj[label];
2953             }
2954
2955             if (isDefault) {
2956                 this.hasDefaultHeaders = true;
2957             }
2958             else {
2959                 this.hasHeaders = true;
2960             }
2961         },
2962
2963
2964         setHeader:function(o)
2965         {
2966             if (this.hasDefaultHeaders) {
2967                 for (var prop in this.defaultHeaders) {
2968                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2969                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2970                     }
2971                 }
2972             }
2973
2974             if (this.hasHeaders) {
2975                 for (var prop in this.headers) {
2976                     if (this.headers.hasOwnProperty(prop)) {
2977                         o.conn.setRequestHeader(prop, this.headers[prop]);
2978                     }
2979                 }
2980                 this.headers = {};
2981                 this.hasHeaders = false;
2982             }
2983         },
2984
2985         resetDefaultHeaders:function() {
2986             delete this.defaultHeaders;
2987             this.defaultHeaders = {};
2988             this.hasDefaultHeaders = false;
2989         },
2990
2991         abort:function(o, callback, isTimeout)
2992         {
2993             if(this.isCallInProgress(o)) {
2994                 o.conn.abort();
2995                 window.clearInterval(this.poll[o.tId]);
2996                 delete this.poll[o.tId];
2997                 if (isTimeout) {
2998                     delete this.timeout[o.tId];
2999                 }
3000
3001                 this.handleTransactionResponse(o, callback, true);
3002
3003                 return true;
3004             }
3005             else {
3006                 return false;
3007             }
3008         },
3009
3010
3011         isCallInProgress:function(o)
3012         {
3013             if (o && o.conn) {
3014                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3015             }
3016             else {
3017
3018                 return false;
3019             }
3020         },
3021
3022
3023         releaseObject:function(o)
3024         {
3025
3026             o.conn = null;
3027
3028             o = null;
3029         },
3030
3031         activeX:[
3032         'MSXML2.XMLHTTP.3.0',
3033         'MSXML2.XMLHTTP',
3034         'Microsoft.XMLHTTP'
3035         ]
3036
3037
3038     };
3039 })();/*
3040  * Portions of this file are based on pieces of Yahoo User Interface Library
3041  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3042  * YUI licensed under the BSD License:
3043  * http://developer.yahoo.net/yui/license.txt
3044  * <script type="text/javascript">
3045  *
3046  */
3047
3048 Roo.lib.Region = function(t, r, b, l) {
3049     this.top = t;
3050     this[1] = t;
3051     this.right = r;
3052     this.bottom = b;
3053     this.left = l;
3054     this[0] = l;
3055 };
3056
3057
3058 Roo.lib.Region.prototype = {
3059     contains : function(region) {
3060         return ( region.left >= this.left &&
3061                  region.right <= this.right &&
3062                  region.top >= this.top &&
3063                  region.bottom <= this.bottom    );
3064
3065     },
3066
3067     getArea : function() {
3068         return ( (this.bottom - this.top) * (this.right - this.left) );
3069     },
3070
3071     intersect : function(region) {
3072         var t = Math.max(this.top, region.top);
3073         var r = Math.min(this.right, region.right);
3074         var b = Math.min(this.bottom, region.bottom);
3075         var l = Math.max(this.left, region.left);
3076
3077         if (b >= t && r >= l) {
3078             return new Roo.lib.Region(t, r, b, l);
3079         } else {
3080             return null;
3081         }
3082     },
3083     union : function(region) {
3084         var t = Math.min(this.top, region.top);
3085         var r = Math.max(this.right, region.right);
3086         var b = Math.max(this.bottom, region.bottom);
3087         var l = Math.min(this.left, region.left);
3088
3089         return new Roo.lib.Region(t, r, b, l);
3090     },
3091
3092     adjust : function(t, l, b, r) {
3093         this.top += t;
3094         this.left += l;
3095         this.right += r;
3096         this.bottom += b;
3097         return this;
3098     }
3099 };
3100
3101 Roo.lib.Region.getRegion = function(el) {
3102     var p = Roo.lib.Dom.getXY(el);
3103
3104     var t = p[1];
3105     var r = p[0] + el.offsetWidth;
3106     var b = p[1] + el.offsetHeight;
3107     var l = p[0];
3108
3109     return new Roo.lib.Region(t, r, b, l);
3110 };
3111 /*
3112  * Portions of this file are based on pieces of Yahoo User Interface Library
3113  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3114  * YUI licensed under the BSD License:
3115  * http://developer.yahoo.net/yui/license.txt
3116  * <script type="text/javascript">
3117  *
3118  */
3119 //@@dep Roo.lib.Region
3120
3121
3122 Roo.lib.Point = function(x, y) {
3123     if (x instanceof Array) {
3124         y = x[1];
3125         x = x[0];
3126     }
3127     this.x = this.right = this.left = this[0] = x;
3128     this.y = this.top = this.bottom = this[1] = y;
3129 };
3130
3131 Roo.lib.Point.prototype = new Roo.lib.Region();
3132 /*
3133  * Portions of this file are based on pieces of Yahoo User Interface Library
3134  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3135  * YUI licensed under the BSD License:
3136  * http://developer.yahoo.net/yui/license.txt
3137  * <script type="text/javascript">
3138  *
3139  */
3140  
3141 (function() {   
3142
3143     Roo.lib.Anim = {
3144         scroll : function(el, args, duration, easing, cb, scope) {
3145             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3146         },
3147
3148         motion : function(el, args, duration, easing, cb, scope) {
3149             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3150         },
3151
3152         color : function(el, args, duration, easing, cb, scope) {
3153             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3154         },
3155
3156         run : function(el, args, duration, easing, cb, scope, type) {
3157             type = type || Roo.lib.AnimBase;
3158             if (typeof easing == "string") {
3159                 easing = Roo.lib.Easing[easing];
3160             }
3161             var anim = new type(el, args, duration, easing);
3162             anim.animateX(function() {
3163                 Roo.callback(cb, scope);
3164             });
3165             return anim;
3166         }
3167     };
3168 })();/*
3169  * Portions of this file are based on pieces of Yahoo User Interface Library
3170  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3171  * YUI licensed under the BSD License:
3172  * http://developer.yahoo.net/yui/license.txt
3173  * <script type="text/javascript">
3174  *
3175  */
3176
3177 (function() {    
3178     var libFlyweight;
3179     
3180     function fly(el) {
3181         if (!libFlyweight) {
3182             libFlyweight = new Roo.Element.Flyweight();
3183         }
3184         libFlyweight.dom = el;
3185         return libFlyweight;
3186     }
3187
3188     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3189     
3190    
3191     
3192     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3193         if (el) {
3194             this.init(el, attributes, duration, method);
3195         }
3196     };
3197
3198     Roo.lib.AnimBase.fly = fly;
3199     
3200     
3201     
3202     Roo.lib.AnimBase.prototype = {
3203
3204         toString: function() {
3205             var el = this.getEl();
3206             var id = el.id || el.tagName;
3207             return ("Anim " + id);
3208         },
3209
3210         patterns: {
3211             noNegatives:        /width|height|opacity|padding/i,
3212             offsetAttribute:  /^((width|height)|(top|left))$/,
3213             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3214             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3215         },
3216
3217
3218         doMethod: function(attr, start, end) {
3219             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3220         },
3221
3222
3223         setAttribute: function(attr, val, unit) {
3224             if (this.patterns.noNegatives.test(attr)) {
3225                 val = (val > 0) ? val : 0;
3226             }
3227
3228             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3229         },
3230
3231
3232         getAttribute: function(attr) {
3233             var el = this.getEl();
3234             var val = fly(el).getStyle(attr);
3235
3236             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3237                 return parseFloat(val);
3238             }
3239
3240             var a = this.patterns.offsetAttribute.exec(attr) || [];
3241             var pos = !!( a[3] );
3242             var box = !!( a[2] );
3243
3244
3245             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3246                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3247             } else {
3248                 val = 0;
3249             }
3250
3251             return val;
3252         },
3253
3254
3255         getDefaultUnit: function(attr) {
3256             if (this.patterns.defaultUnit.test(attr)) {
3257                 return 'px';
3258             }
3259
3260             return '';
3261         },
3262
3263         animateX : function(callback, scope) {
3264             var f = function() {
3265                 this.onComplete.removeListener(f);
3266                 if (typeof callback == "function") {
3267                     callback.call(scope || this, this);
3268                 }
3269             };
3270             this.onComplete.addListener(f, this);
3271             this.animate();
3272         },
3273
3274
3275         setRuntimeAttribute: function(attr) {
3276             var start;
3277             var end;
3278             var attributes = this.attributes;
3279
3280             this.runtimeAttributes[attr] = {};
3281
3282             var isset = function(prop) {
3283                 return (typeof prop !== 'undefined');
3284             };
3285
3286             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3287                 return false;
3288             }
3289
3290             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3291
3292
3293             if (isset(attributes[attr]['to'])) {
3294                 end = attributes[attr]['to'];
3295             } else if (isset(attributes[attr]['by'])) {
3296                 if (start.constructor == Array) {
3297                     end = [];
3298                     for (var i = 0, len = start.length; i < len; ++i) {
3299                         end[i] = start[i] + attributes[attr]['by'][i];
3300                     }
3301                 } else {
3302                     end = start + attributes[attr]['by'];
3303                 }
3304             }
3305
3306             this.runtimeAttributes[attr].start = start;
3307             this.runtimeAttributes[attr].end = end;
3308
3309
3310             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3311         },
3312
3313
3314         init: function(el, attributes, duration, method) {
3315
3316             var isAnimated = false;
3317
3318
3319             var startTime = null;
3320
3321
3322             var actualFrames = 0;
3323
3324
3325             el = Roo.getDom(el);
3326
3327
3328             this.attributes = attributes || {};
3329
3330
3331             this.duration = duration || 1;
3332
3333
3334             this.method = method || Roo.lib.Easing.easeNone;
3335
3336
3337             this.useSeconds = true;
3338
3339
3340             this.currentFrame = 0;
3341
3342
3343             this.totalFrames = Roo.lib.AnimMgr.fps;
3344
3345
3346             this.getEl = function() {
3347                 return el;
3348             };
3349
3350
3351             this.isAnimated = function() {
3352                 return isAnimated;
3353             };
3354
3355
3356             this.getStartTime = function() {
3357                 return startTime;
3358             };
3359
3360             this.runtimeAttributes = {};
3361
3362
3363             this.animate = function() {
3364                 if (this.isAnimated()) {
3365                     return false;
3366                 }
3367
3368                 this.currentFrame = 0;
3369
3370                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3371
3372                 Roo.lib.AnimMgr.registerElement(this);
3373             };
3374
3375
3376             this.stop = function(finish) {
3377                 if (finish) {
3378                     this.currentFrame = this.totalFrames;
3379                     this._onTween.fire();
3380                 }
3381                 Roo.lib.AnimMgr.stop(this);
3382             };
3383
3384             var onStart = function() {
3385                 this.onStart.fire();
3386
3387                 this.runtimeAttributes = {};
3388                 for (var attr in this.attributes) {
3389                     this.setRuntimeAttribute(attr);
3390                 }
3391
3392                 isAnimated = true;
3393                 actualFrames = 0;
3394                 startTime = new Date();
3395             };
3396
3397
3398             var onTween = function() {
3399                 var data = {
3400                     duration: new Date() - this.getStartTime(),
3401                     currentFrame: this.currentFrame
3402                 };
3403
3404                 data.toString = function() {
3405                     return (
3406                             'duration: ' + data.duration +
3407                             ', currentFrame: ' + data.currentFrame
3408                             );
3409                 };
3410
3411                 this.onTween.fire(data);
3412
3413                 var runtimeAttributes = this.runtimeAttributes;
3414
3415                 for (var attr in runtimeAttributes) {
3416                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3417                 }
3418
3419                 actualFrames += 1;
3420             };
3421
3422             var onComplete = function() {
3423                 var actual_duration = (new Date() - startTime) / 1000 ;
3424
3425                 var data = {
3426                     duration: actual_duration,
3427                     frames: actualFrames,
3428                     fps: actualFrames / actual_duration
3429                 };
3430
3431                 data.toString = function() {
3432                     return (
3433                             'duration: ' + data.duration +
3434                             ', frames: ' + data.frames +
3435                             ', fps: ' + data.fps
3436                             );
3437                 };
3438
3439                 isAnimated = false;
3440                 actualFrames = 0;
3441                 this.onComplete.fire(data);
3442             };
3443
3444
3445             this._onStart = new Roo.util.Event(this);
3446             this.onStart = new Roo.util.Event(this);
3447             this.onTween = new Roo.util.Event(this);
3448             this._onTween = new Roo.util.Event(this);
3449             this.onComplete = new Roo.util.Event(this);
3450             this._onComplete = new Roo.util.Event(this);
3451             this._onStart.addListener(onStart);
3452             this._onTween.addListener(onTween);
3453             this._onComplete.addListener(onComplete);
3454         }
3455     };
3456 })();
3457 /*
3458  * Portions of this file are based on pieces of Yahoo User Interface Library
3459  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3460  * YUI licensed under the BSD License:
3461  * http://developer.yahoo.net/yui/license.txt
3462  * <script type="text/javascript">
3463  *
3464  */
3465
3466 Roo.lib.AnimMgr = new function() {
3467
3468     var thread = null;
3469
3470
3471     var queue = [];
3472
3473
3474     var tweenCount = 0;
3475
3476
3477     this.fps = 1000;
3478
3479
3480     this.delay = 1;
3481
3482
3483     this.registerElement = function(tween) {
3484         queue[queue.length] = tween;
3485         tweenCount += 1;
3486         tween._onStart.fire();
3487         this.start();
3488     };
3489
3490
3491     this.unRegister = function(tween, index) {
3492         tween._onComplete.fire();
3493         index = index || getIndex(tween);
3494         if (index != -1) {
3495             queue.splice(index, 1);
3496         }
3497
3498         tweenCount -= 1;
3499         if (tweenCount <= 0) {
3500             this.stop();
3501         }
3502     };
3503
3504
3505     this.start = function() {
3506         if (thread === null) {
3507             thread = setInterval(this.run, this.delay);
3508         }
3509     };
3510
3511
3512     this.stop = function(tween) {
3513         if (!tween) {
3514             clearInterval(thread);
3515
3516             for (var i = 0, len = queue.length; i < len; ++i) {
3517                 if (queue[0].isAnimated()) {
3518                     this.unRegister(queue[0], 0);
3519                 }
3520             }
3521
3522             queue = [];
3523             thread = null;
3524             tweenCount = 0;
3525         }
3526         else {
3527             this.unRegister(tween);
3528         }
3529     };
3530
3531
3532     this.run = function() {
3533         for (var i = 0, len = queue.length; i < len; ++i) {
3534             var tween = queue[i];
3535             if (!tween || !tween.isAnimated()) {
3536                 continue;
3537             }
3538
3539             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3540             {
3541                 tween.currentFrame += 1;
3542
3543                 if (tween.useSeconds) {
3544                     correctFrame(tween);
3545                 }
3546                 tween._onTween.fire();
3547             }
3548             else {
3549                 Roo.lib.AnimMgr.stop(tween, i);
3550             }
3551         }
3552     };
3553
3554     var getIndex = function(anim) {
3555         for (var i = 0, len = queue.length; i < len; ++i) {
3556             if (queue[i] == anim) {
3557                 return i;
3558             }
3559         }
3560         return -1;
3561     };
3562
3563
3564     var correctFrame = function(tween) {
3565         var frames = tween.totalFrames;
3566         var frame = tween.currentFrame;
3567         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3568         var elapsed = (new Date() - tween.getStartTime());
3569         var tweak = 0;
3570
3571         if (elapsed < tween.duration * 1000) {
3572             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3573         } else {
3574             tweak = frames - (frame + 1);
3575         }
3576         if (tweak > 0 && isFinite(tweak)) {
3577             if (tween.currentFrame + tweak >= frames) {
3578                 tweak = frames - (frame + 1);
3579             }
3580
3581             tween.currentFrame += tweak;
3582         }
3583     };
3584 };
3585
3586     /*
3587  * Portions of this file are based on pieces of Yahoo User Interface Library
3588  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3589  * YUI licensed under the BSD License:
3590  * http://developer.yahoo.net/yui/license.txt
3591  * <script type="text/javascript">
3592  *
3593  */
3594 Roo.lib.Bezier = new function() {
3595
3596         this.getPosition = function(points, t) {
3597             var n = points.length;
3598             var tmp = [];
3599
3600             for (var i = 0; i < n; ++i) {
3601                 tmp[i] = [points[i][0], points[i][1]];
3602             }
3603
3604             for (var j = 1; j < n; ++j) {
3605                 for (i = 0; i < n - j; ++i) {
3606                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3607                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3608                 }
3609             }
3610
3611             return [ tmp[0][0], tmp[0][1] ];
3612
3613         };
3614     };/*
3615  * Portions of this file are based on pieces of Yahoo User Interface Library
3616  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3617  * YUI licensed under the BSD License:
3618  * http://developer.yahoo.net/yui/license.txt
3619  * <script type="text/javascript">
3620  *
3621  */
3622 (function() {
3623
3624     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3625         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3626     };
3627
3628     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3629
3630     var fly = Roo.lib.AnimBase.fly;
3631     var Y = Roo.lib;
3632     var superclass = Y.ColorAnim.superclass;
3633     var proto = Y.ColorAnim.prototype;
3634
3635     proto.toString = function() {
3636         var el = this.getEl();
3637         var id = el.id || el.tagName;
3638         return ("ColorAnim " + id);
3639     };
3640
3641     proto.patterns.color = /color$/i;
3642     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3643     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3644     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3645     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3646
3647
3648     proto.parseColor = function(s) {
3649         if (s.length == 3) {
3650             return s;
3651         }
3652
3653         var c = this.patterns.hex.exec(s);
3654         if (c && c.length == 4) {
3655             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3656         }
3657
3658         c = this.patterns.rgb.exec(s);
3659         if (c && c.length == 4) {
3660             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3661         }
3662
3663         c = this.patterns.hex3.exec(s);
3664         if (c && c.length == 4) {
3665             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3666         }
3667
3668         return null;
3669     };
3670     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3671     proto.getAttribute = function(attr) {
3672         var el = this.getEl();
3673         if (this.patterns.color.test(attr)) {
3674             var val = fly(el).getStyle(attr);
3675
3676             if (this.patterns.transparent.test(val)) {
3677                 var parent = el.parentNode;
3678                 val = fly(parent).getStyle(attr);
3679
3680                 while (parent && this.patterns.transparent.test(val)) {
3681                     parent = parent.parentNode;
3682                     val = fly(parent).getStyle(attr);
3683                     if (parent.tagName.toUpperCase() == 'HTML') {
3684                         val = '#fff';
3685                     }
3686                 }
3687             }
3688         } else {
3689             val = superclass.getAttribute.call(this, attr);
3690         }
3691
3692         return val;
3693     };
3694     proto.getAttribute = function(attr) {
3695         var el = this.getEl();
3696         if (this.patterns.color.test(attr)) {
3697             var val = fly(el).getStyle(attr);
3698
3699             if (this.patterns.transparent.test(val)) {
3700                 var parent = el.parentNode;
3701                 val = fly(parent).getStyle(attr);
3702
3703                 while (parent && this.patterns.transparent.test(val)) {
3704                     parent = parent.parentNode;
3705                     val = fly(parent).getStyle(attr);
3706                     if (parent.tagName.toUpperCase() == 'HTML') {
3707                         val = '#fff';
3708                     }
3709                 }
3710             }
3711         } else {
3712             val = superclass.getAttribute.call(this, attr);
3713         }
3714
3715         return val;
3716     };
3717
3718     proto.doMethod = function(attr, start, end) {
3719         var val;
3720
3721         if (this.patterns.color.test(attr)) {
3722             val = [];
3723             for (var i = 0, len = start.length; i < len; ++i) {
3724                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3725             }
3726
3727             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3728         }
3729         else {
3730             val = superclass.doMethod.call(this, attr, start, end);
3731         }
3732
3733         return val;
3734     };
3735
3736     proto.setRuntimeAttribute = function(attr) {
3737         superclass.setRuntimeAttribute.call(this, attr);
3738
3739         if (this.patterns.color.test(attr)) {
3740             var attributes = this.attributes;
3741             var start = this.parseColor(this.runtimeAttributes[attr].start);
3742             var end = this.parseColor(this.runtimeAttributes[attr].end);
3743
3744             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3745                 end = this.parseColor(attributes[attr].by);
3746
3747                 for (var i = 0, len = start.length; i < len; ++i) {
3748                     end[i] = start[i] + end[i];
3749                 }
3750             }
3751
3752             this.runtimeAttributes[attr].start = start;
3753             this.runtimeAttributes[attr].end = end;
3754         }
3755     };
3756 })();
3757
3758 /*
3759  * Portions of this file are based on pieces of Yahoo User Interface Library
3760  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3761  * YUI licensed under the BSD License:
3762  * http://developer.yahoo.net/yui/license.txt
3763  * <script type="text/javascript">
3764  *
3765  */
3766 Roo.lib.Easing = {
3767
3768
3769     easeNone: function (t, b, c, d) {
3770         return c * t / d + b;
3771     },
3772
3773
3774     easeIn: function (t, b, c, d) {
3775         return c * (t /= d) * t + b;
3776     },
3777
3778
3779     easeOut: function (t, b, c, d) {
3780         return -c * (t /= d) * (t - 2) + b;
3781     },
3782
3783
3784     easeBoth: function (t, b, c, d) {
3785         if ((t /= d / 2) < 1) {
3786             return c / 2 * t * t + b;
3787         }
3788
3789         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3790     },
3791
3792
3793     easeInStrong: function (t, b, c, d) {
3794         return c * (t /= d) * t * t * t + b;
3795     },
3796
3797
3798     easeOutStrong: function (t, b, c, d) {
3799         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3800     },
3801
3802
3803     easeBothStrong: function (t, b, c, d) {
3804         if ((t /= d / 2) < 1) {
3805             return c / 2 * t * t * t * t + b;
3806         }
3807
3808         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3809     },
3810
3811
3812
3813     elasticIn: function (t, b, c, d, a, p) {
3814         if (t == 0) {
3815             return b;
3816         }
3817         if ((t /= d) == 1) {
3818             return b + c;
3819         }
3820         if (!p) {
3821             p = d * .3;
3822         }
3823
3824         if (!a || a < Math.abs(c)) {
3825             a = c;
3826             var s = p / 4;
3827         }
3828         else {
3829             var s = p / (2 * Math.PI) * Math.asin(c / a);
3830         }
3831
3832         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3833     },
3834
3835
3836     elasticOut: function (t, b, c, d, a, p) {
3837         if (t == 0) {
3838             return b;
3839         }
3840         if ((t /= d) == 1) {
3841             return b + c;
3842         }
3843         if (!p) {
3844             p = d * .3;
3845         }
3846
3847         if (!a || a < Math.abs(c)) {
3848             a = c;
3849             var s = p / 4;
3850         }
3851         else {
3852             var s = p / (2 * Math.PI) * Math.asin(c / a);
3853         }
3854
3855         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3856     },
3857
3858
3859     elasticBoth: function (t, b, c, d, a, p) {
3860         if (t == 0) {
3861             return b;
3862         }
3863
3864         if ((t /= d / 2) == 2) {
3865             return b + c;
3866         }
3867
3868         if (!p) {
3869             p = d * (.3 * 1.5);
3870         }
3871
3872         if (!a || a < Math.abs(c)) {
3873             a = c;
3874             var s = p / 4;
3875         }
3876         else {
3877             var s = p / (2 * Math.PI) * Math.asin(c / a);
3878         }
3879
3880         if (t < 1) {
3881             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3882                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3883         }
3884         return a * Math.pow(2, -10 * (t -= 1)) *
3885                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3886     },
3887
3888
3889
3890     backIn: function (t, b, c, d, s) {
3891         if (typeof s == 'undefined') {
3892             s = 1.70158;
3893         }
3894         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3895     },
3896
3897
3898     backOut: function (t, b, c, d, s) {
3899         if (typeof s == 'undefined') {
3900             s = 1.70158;
3901         }
3902         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3903     },
3904
3905
3906     backBoth: function (t, b, c, d, s) {
3907         if (typeof s == 'undefined') {
3908             s = 1.70158;
3909         }
3910
3911         if ((t /= d / 2 ) < 1) {
3912             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3913         }
3914         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3915     },
3916
3917
3918     bounceIn: function (t, b, c, d) {
3919         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3920     },
3921
3922
3923     bounceOut: function (t, b, c, d) {
3924         if ((t /= d) < (1 / 2.75)) {
3925             return c * (7.5625 * t * t) + b;
3926         } else if (t < (2 / 2.75)) {
3927             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3928         } else if (t < (2.5 / 2.75)) {
3929             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3930         }
3931         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3932     },
3933
3934
3935     bounceBoth: function (t, b, c, d) {
3936         if (t < d / 2) {
3937             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3938         }
3939         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3940     }
3941 };/*
3942  * Portions of this file are based on pieces of Yahoo User Interface Library
3943  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3944  * YUI licensed under the BSD License:
3945  * http://developer.yahoo.net/yui/license.txt
3946  * <script type="text/javascript">
3947  *
3948  */
3949     (function() {
3950         Roo.lib.Motion = function(el, attributes, duration, method) {
3951             if (el) {
3952                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3953             }
3954         };
3955
3956         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3957
3958
3959         var Y = Roo.lib;
3960         var superclass = Y.Motion.superclass;
3961         var proto = Y.Motion.prototype;
3962
3963         proto.toString = function() {
3964             var el = this.getEl();
3965             var id = el.id || el.tagName;
3966             return ("Motion " + id);
3967         };
3968
3969         proto.patterns.points = /^points$/i;
3970
3971         proto.setAttribute = function(attr, val, unit) {
3972             if (this.patterns.points.test(attr)) {
3973                 unit = unit || 'px';
3974                 superclass.setAttribute.call(this, 'left', val[0], unit);
3975                 superclass.setAttribute.call(this, 'top', val[1], unit);
3976             } else {
3977                 superclass.setAttribute.call(this, attr, val, unit);
3978             }
3979         };
3980
3981         proto.getAttribute = function(attr) {
3982             if (this.patterns.points.test(attr)) {
3983                 var val = [
3984                         superclass.getAttribute.call(this, 'left'),
3985                         superclass.getAttribute.call(this, 'top')
3986                         ];
3987             } else {
3988                 val = superclass.getAttribute.call(this, attr);
3989             }
3990
3991             return val;
3992         };
3993
3994         proto.doMethod = function(attr, start, end) {
3995             var val = null;
3996
3997             if (this.patterns.points.test(attr)) {
3998                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3999                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4000             } else {
4001                 val = superclass.doMethod.call(this, attr, start, end);
4002             }
4003             return val;
4004         };
4005
4006         proto.setRuntimeAttribute = function(attr) {
4007             if (this.patterns.points.test(attr)) {
4008                 var el = this.getEl();
4009                 var attributes = this.attributes;
4010                 var start;
4011                 var control = attributes['points']['control'] || [];
4012                 var end;
4013                 var i, len;
4014
4015                 if (control.length > 0 && !(control[0] instanceof Array)) {
4016                     control = [control];
4017                 } else {
4018                     var tmp = [];
4019                     for (i = 0,len = control.length; i < len; ++i) {
4020                         tmp[i] = control[i];
4021                     }
4022                     control = tmp;
4023                 }
4024
4025                 Roo.fly(el).position();
4026
4027                 if (isset(attributes['points']['from'])) {
4028                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4029                 }
4030                 else {
4031                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4032                 }
4033
4034                 start = this.getAttribute('points');
4035
4036
4037                 if (isset(attributes['points']['to'])) {
4038                     end = translateValues.call(this, attributes['points']['to'], start);
4039
4040                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4041                     for (i = 0,len = control.length; i < len; ++i) {
4042                         control[i] = translateValues.call(this, control[i], start);
4043                     }
4044
4045
4046                 } else if (isset(attributes['points']['by'])) {
4047                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4048
4049                     for (i = 0,len = control.length; i < len; ++i) {
4050                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4051                     }
4052                 }
4053
4054                 this.runtimeAttributes[attr] = [start];
4055
4056                 if (control.length > 0) {
4057                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4058                 }
4059
4060                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4061             }
4062             else {
4063                 superclass.setRuntimeAttribute.call(this, attr);
4064             }
4065         };
4066
4067         var translateValues = function(val, start) {
4068             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4069             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4070
4071             return val;
4072         };
4073
4074         var isset = function(prop) {
4075             return (typeof prop !== 'undefined');
4076         };
4077     })();
4078 /*
4079  * Portions of this file are based on pieces of Yahoo User Interface Library
4080  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4081  * YUI licensed under the BSD License:
4082  * http://developer.yahoo.net/yui/license.txt
4083  * <script type="text/javascript">
4084  *
4085  */
4086     (function() {
4087         Roo.lib.Scroll = function(el, attributes, duration, method) {
4088             if (el) {
4089                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4090             }
4091         };
4092
4093         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4094
4095
4096         var Y = Roo.lib;
4097         var superclass = Y.Scroll.superclass;
4098         var proto = Y.Scroll.prototype;
4099
4100         proto.toString = function() {
4101             var el = this.getEl();
4102             var id = el.id || el.tagName;
4103             return ("Scroll " + id);
4104         };
4105
4106         proto.doMethod = function(attr, start, end) {
4107             var val = null;
4108
4109             if (attr == 'scroll') {
4110                 val = [
4111                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4112                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4113                         ];
4114
4115             } else {
4116                 val = superclass.doMethod.call(this, attr, start, end);
4117             }
4118             return val;
4119         };
4120
4121         proto.getAttribute = function(attr) {
4122             var val = null;
4123             var el = this.getEl();
4124
4125             if (attr == 'scroll') {
4126                 val = [ el.scrollLeft, el.scrollTop ];
4127             } else {
4128                 val = superclass.getAttribute.call(this, attr);
4129             }
4130
4131             return val;
4132         };
4133
4134         proto.setAttribute = function(attr, val, unit) {
4135             var el = this.getEl();
4136
4137             if (attr == 'scroll') {
4138                 el.scrollLeft = val[0];
4139                 el.scrollTop = val[1];
4140             } else {
4141                 superclass.setAttribute.call(this, attr, val, unit);
4142             }
4143         };
4144     })();
4145 /*
4146  * Based on:
4147  * Ext JS Library 1.1.1
4148  * Copyright(c) 2006-2007, Ext JS, LLC.
4149  *
4150  * Originally Released Under LGPL - original licence link has changed is not relivant.
4151  *
4152  * Fork - LGPL
4153  * <script type="text/javascript">
4154  */
4155
4156
4157 // nasty IE9 hack - what a pile of crap that is..
4158
4159  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4160     Range.prototype.createContextualFragment = function (html) {
4161         var doc = window.document;
4162         var container = doc.createElement("div");
4163         container.innerHTML = html;
4164         var frag = doc.createDocumentFragment(), n;
4165         while ((n = container.firstChild)) {
4166             frag.appendChild(n);
4167         }
4168         return frag;
4169     };
4170 }
4171
4172 /**
4173  * @class Roo.DomHelper
4174  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4175  * 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>.
4176  * @singleton
4177  */
4178 Roo.DomHelper = function(){
4179     var tempTableEl = null;
4180     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4181     var tableRe = /^table|tbody|tr|td$/i;
4182     var xmlns = {};
4183     // build as innerHTML where available
4184     /** @ignore */
4185     var createHtml = function(o){
4186         if(typeof o == 'string'){
4187             return o;
4188         }
4189         var b = "";
4190         if(!o.tag){
4191             o.tag = "div";
4192         }
4193         b += "<" + o.tag;
4194         for(var attr in o){
4195             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4196             if(attr == "style"){
4197                 var s = o["style"];
4198                 if(typeof s == "function"){
4199                     s = s.call();
4200                 }
4201                 if(typeof s == "string"){
4202                     b += ' style="' + s + '"';
4203                 }else if(typeof s == "object"){
4204                     b += ' style="';
4205                     for(var key in s){
4206                         if(typeof s[key] != "function"){
4207                             b += key + ":" + s[key] + ";";
4208                         }
4209                     }
4210                     b += '"';
4211                 }
4212             }else{
4213                 if(attr == "cls"){
4214                     b += ' class="' + o["cls"] + '"';
4215                 }else if(attr == "htmlFor"){
4216                     b += ' for="' + o["htmlFor"] + '"';
4217                 }else{
4218                     b += " " + attr + '="' + o[attr] + '"';
4219                 }
4220             }
4221         }
4222         if(emptyTags.test(o.tag)){
4223             b += "/>";
4224         }else{
4225             b += ">";
4226             var cn = o.children || o.cn;
4227             if(cn){
4228                 //http://bugs.kde.org/show_bug.cgi?id=71506
4229                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4230                     for(var i = 0, len = cn.length; i < len; i++) {
4231                         b += createHtml(cn[i], b);
4232                     }
4233                 }else{
4234                     b += createHtml(cn, b);
4235                 }
4236             }
4237             if(o.html){
4238                 b += o.html;
4239             }
4240             b += "</" + o.tag + ">";
4241         }
4242         return b;
4243     };
4244
4245     // build as dom
4246     /** @ignore */
4247     var createDom = function(o, parentNode){
4248          
4249         // defininition craeted..
4250         var ns = false;
4251         if (o.ns && o.ns != 'html') {
4252                
4253             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4254                 xmlns[o.ns] = o.xmlns;
4255                 ns = o.xmlns;
4256             }
4257             if (typeof(xmlns[o.ns]) == 'undefined') {
4258                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4259             }
4260             ns = xmlns[o.ns];
4261         }
4262         
4263         
4264         if (typeof(o) == 'string') {
4265             return parentNode.appendChild(document.createTextNode(o));
4266         }
4267         o.tag = o.tag || div;
4268         if (o.ns && Roo.isIE) {
4269             ns = false;
4270             o.tag = o.ns + ':' + o.tag;
4271             
4272         }
4273         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4274         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4275         for(var attr in o){
4276             
4277             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4278                     attr == "style" || typeof o[attr] == "function") { continue; }
4279                     
4280             if(attr=="cls" && Roo.isIE){
4281                 el.className = o["cls"];
4282             }else{
4283                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4284                 else { 
4285                     el[attr] = o[attr];
4286                 }
4287             }
4288         }
4289         Roo.DomHelper.applyStyles(el, o.style);
4290         var cn = o.children || o.cn;
4291         if(cn){
4292             //http://bugs.kde.org/show_bug.cgi?id=71506
4293              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4294                 for(var i = 0, len = cn.length; i < len; i++) {
4295                     createDom(cn[i], el);
4296                 }
4297             }else{
4298                 createDom(cn, el);
4299             }
4300         }
4301         if(o.html){
4302             el.innerHTML = o.html;
4303         }
4304         if(parentNode){
4305            parentNode.appendChild(el);
4306         }
4307         return el;
4308     };
4309
4310     var ieTable = function(depth, s, h, e){
4311         tempTableEl.innerHTML = [s, h, e].join('');
4312         var i = -1, el = tempTableEl;
4313         while(++i < depth){
4314             el = el.firstChild;
4315         }
4316         return el;
4317     };
4318
4319     // kill repeat to save bytes
4320     var ts = '<table>',
4321         te = '</table>',
4322         tbs = ts+'<tbody>',
4323         tbe = '</tbody>'+te,
4324         trs = tbs + '<tr>',
4325         tre = '</tr>'+tbe;
4326
4327     /**
4328      * @ignore
4329      * Nasty code for IE's broken table implementation
4330      */
4331     var insertIntoTable = function(tag, where, el, html){
4332         if(!tempTableEl){
4333             tempTableEl = document.createElement('div');
4334         }
4335         var node;
4336         var before = null;
4337         if(tag == 'td'){
4338             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4339                 return;
4340             }
4341             if(where == 'beforebegin'){
4342                 before = el;
4343                 el = el.parentNode;
4344             } else{
4345                 before = el.nextSibling;
4346                 el = el.parentNode;
4347             }
4348             node = ieTable(4, trs, html, tre);
4349         }
4350         else if(tag == 'tr'){
4351             if(where == 'beforebegin'){
4352                 before = el;
4353                 el = el.parentNode;
4354                 node = ieTable(3, tbs, html, tbe);
4355             } else if(where == 'afterend'){
4356                 before = el.nextSibling;
4357                 el = el.parentNode;
4358                 node = ieTable(3, tbs, html, tbe);
4359             } else{ // INTO a TR
4360                 if(where == 'afterbegin'){
4361                     before = el.firstChild;
4362                 }
4363                 node = ieTable(4, trs, html, tre);
4364             }
4365         } else if(tag == 'tbody'){
4366             if(where == 'beforebegin'){
4367                 before = el;
4368                 el = el.parentNode;
4369                 node = ieTable(2, ts, html, te);
4370             } else if(where == 'afterend'){
4371                 before = el.nextSibling;
4372                 el = el.parentNode;
4373                 node = ieTable(2, ts, html, te);
4374             } else{
4375                 if(where == 'afterbegin'){
4376                     before = el.firstChild;
4377                 }
4378                 node = ieTable(3, tbs, html, tbe);
4379             }
4380         } else{ // TABLE
4381             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4382                 return;
4383             }
4384             if(where == 'afterbegin'){
4385                 before = el.firstChild;
4386             }
4387             node = ieTable(2, ts, html, te);
4388         }
4389         el.insertBefore(node, before);
4390         return node;
4391     };
4392
4393     return {
4394     /** True to force the use of DOM instead of html fragments @type Boolean */
4395     useDom : false,
4396
4397     /**
4398      * Returns the markup for the passed Element(s) config
4399      * @param {Object} o The Dom object spec (and children)
4400      * @return {String}
4401      */
4402     markup : function(o){
4403         return createHtml(o);
4404     },
4405
4406     /**
4407      * Applies a style specification to an element
4408      * @param {String/HTMLElement} el The element to apply styles to
4409      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4410      * a function which returns such a specification.
4411      */
4412     applyStyles : function(el, styles){
4413         if(styles){
4414            el = Roo.fly(el);
4415            if(typeof styles == "string"){
4416                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4417                var matches;
4418                while ((matches = re.exec(styles)) != null){
4419                    el.setStyle(matches[1], matches[2]);
4420                }
4421            }else if (typeof styles == "object"){
4422                for (var style in styles){
4423                   el.setStyle(style, styles[style]);
4424                }
4425            }else if (typeof styles == "function"){
4426                 Roo.DomHelper.applyStyles(el, styles.call());
4427            }
4428         }
4429     },
4430
4431     /**
4432      * Inserts an HTML fragment into the Dom
4433      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4434      * @param {HTMLElement} el The context element
4435      * @param {String} html The HTML fragmenet
4436      * @return {HTMLElement} The new node
4437      */
4438     insertHtml : function(where, el, html){
4439         where = where.toLowerCase();
4440         if(el.insertAdjacentHTML){
4441             if(tableRe.test(el.tagName)){
4442                 var rs;
4443                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4444                     return rs;
4445                 }
4446             }
4447             switch(where){
4448                 case "beforebegin":
4449                     el.insertAdjacentHTML('BeforeBegin', html);
4450                     return el.previousSibling;
4451                 case "afterbegin":
4452                     el.insertAdjacentHTML('AfterBegin', html);
4453                     return el.firstChild;
4454                 case "beforeend":
4455                     el.insertAdjacentHTML('BeforeEnd', html);
4456                     return el.lastChild;
4457                 case "afterend":
4458                     el.insertAdjacentHTML('AfterEnd', html);
4459                     return el.nextSibling;
4460             }
4461             throw 'Illegal insertion point -> "' + where + '"';
4462         }
4463         var range = el.ownerDocument.createRange();
4464         var frag;
4465         switch(where){
4466              case "beforebegin":
4467                 range.setStartBefore(el);
4468                 frag = range.createContextualFragment(html);
4469                 el.parentNode.insertBefore(frag, el);
4470                 return el.previousSibling;
4471              case "afterbegin":
4472                 if(el.firstChild){
4473                     range.setStartBefore(el.firstChild);
4474                     frag = range.createContextualFragment(html);
4475                     el.insertBefore(frag, el.firstChild);
4476                     return el.firstChild;
4477                 }else{
4478                     el.innerHTML = html;
4479                     return el.firstChild;
4480                 }
4481             case "beforeend":
4482                 if(el.lastChild){
4483                     range.setStartAfter(el.lastChild);
4484                     frag = range.createContextualFragment(html);
4485                     el.appendChild(frag);
4486                     return el.lastChild;
4487                 }else{
4488                     el.innerHTML = html;
4489                     return el.lastChild;
4490                 }
4491             case "afterend":
4492                 range.setStartAfter(el);
4493                 frag = range.createContextualFragment(html);
4494                 el.parentNode.insertBefore(frag, el.nextSibling);
4495                 return el.nextSibling;
4496             }
4497             throw 'Illegal insertion point -> "' + where + '"';
4498     },
4499
4500     /**
4501      * Creates new Dom element(s) and inserts them before el
4502      * @param {String/HTMLElement/Element} el The context element
4503      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4504      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4505      * @return {HTMLElement/Roo.Element} The new node
4506      */
4507     insertBefore : function(el, o, returnElement){
4508         return this.doInsert(el, o, returnElement, "beforeBegin");
4509     },
4510
4511     /**
4512      * Creates new Dom element(s) and inserts them after el
4513      * @param {String/HTMLElement/Element} el The context element
4514      * @param {Object} o The Dom object spec (and children)
4515      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4516      * @return {HTMLElement/Roo.Element} The new node
4517      */
4518     insertAfter : function(el, o, returnElement){
4519         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4520     },
4521
4522     /**
4523      * Creates new Dom element(s) and inserts them as the first child of el
4524      * @param {String/HTMLElement/Element} el The context element
4525      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4526      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4527      * @return {HTMLElement/Roo.Element} The new node
4528      */
4529     insertFirst : function(el, o, returnElement){
4530         return this.doInsert(el, o, returnElement, "afterBegin");
4531     },
4532
4533     // private
4534     doInsert : function(el, o, returnElement, pos, sibling){
4535         el = Roo.getDom(el);
4536         var newNode;
4537         if(this.useDom || o.ns){
4538             newNode = createDom(o, null);
4539             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4540         }else{
4541             var html = createHtml(o);
4542             newNode = this.insertHtml(pos, el, html);
4543         }
4544         return returnElement ? Roo.get(newNode, true) : newNode;
4545     },
4546
4547     /**
4548      * Creates new Dom element(s) and appends them to el
4549      * @param {String/HTMLElement/Element} el The context element
4550      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4551      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4552      * @return {HTMLElement/Roo.Element} The new node
4553      */
4554     append : function(el, o, returnElement){
4555         el = Roo.getDom(el);
4556         var newNode;
4557         if(this.useDom || o.ns){
4558             newNode = createDom(o, null);
4559             el.appendChild(newNode);
4560         }else{
4561             var html = createHtml(o);
4562             newNode = this.insertHtml("beforeEnd", el, html);
4563         }
4564         return returnElement ? Roo.get(newNode, true) : newNode;
4565     },
4566
4567     /**
4568      * Creates new Dom element(s) and overwrites the contents of el with them
4569      * @param {String/HTMLElement/Element} el The context element
4570      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4571      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4572      * @return {HTMLElement/Roo.Element} The new node
4573      */
4574     overwrite : function(el, o, returnElement){
4575         el = Roo.getDom(el);
4576         if (o.ns) {
4577           
4578             while (el.childNodes.length) {
4579                 el.removeChild(el.firstChild);
4580             }
4581             createDom(o, el);
4582         } else {
4583             el.innerHTML = createHtml(o);   
4584         }
4585         
4586         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4587     },
4588
4589     /**
4590      * Creates a new Roo.DomHelper.Template from the Dom object spec
4591      * @param {Object} o The Dom object spec (and children)
4592      * @return {Roo.DomHelper.Template} The new template
4593      */
4594     createTemplate : function(o){
4595         var html = createHtml(o);
4596         return new Roo.Template(html);
4597     }
4598     };
4599 }();
4600 /*
4601  * Based on:
4602  * Ext JS Library 1.1.1
4603  * Copyright(c) 2006-2007, Ext JS, LLC.
4604  *
4605  * Originally Released Under LGPL - original licence link has changed is not relivant.
4606  *
4607  * Fork - LGPL
4608  * <script type="text/javascript">
4609  */
4610  
4611 /**
4612 * @class Roo.Template
4613 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4614 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4615 * Usage:
4616 <pre><code>
4617 var t = new Roo.Template({
4618     html :  '&lt;div name="{id}"&gt;' + 
4619         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4620         '&lt;/div&gt;',
4621     myformat: function (value, allValues) {
4622         return 'XX' + value;
4623     }
4624 });
4625 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4626 </code></pre>
4627 * For more information see this blog post with examples:
4628 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4629      - Create Elements using DOM, HTML fragments and Templates</a>. 
4630 * @constructor
4631 * @param {Object} cfg - Configuration object.
4632 */
4633 Roo.Template = function(cfg){
4634     // BC!
4635     if(cfg instanceof Array){
4636         cfg = cfg.join("");
4637     }else if(arguments.length > 1){
4638         cfg = Array.prototype.join.call(arguments, "");
4639     }
4640     
4641     
4642     if (typeof(cfg) == 'object') {
4643         Roo.apply(this,cfg)
4644     } else {
4645         // bc
4646         this.html = cfg;
4647     }
4648     if (this.url) {
4649         this.load();
4650     }
4651     
4652 };
4653 Roo.Template.prototype = {
4654     
4655     /**
4656      * @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..
4657      *                    it should be fixed so that template is observable...
4658      */
4659     url : false,
4660     /**
4661      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4662      */
4663     html : '',
4664     /**
4665      * Returns an HTML fragment of this template with the specified values applied.
4666      * @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'})
4667      * @return {String} The HTML fragment
4668      */
4669     applyTemplate : function(values){
4670         Roo.log(["applyTemplate", values]);
4671         try {
4672            
4673             if(this.compiled){
4674                 return this.compiled(values);
4675             }
4676             var useF = this.disableFormats !== true;
4677             var fm = Roo.util.Format, tpl = this;
4678             var fn = function(m, name, format, args){
4679                 if(format && useF){
4680                     if(format.substr(0, 5) == "this."){
4681                         return tpl.call(format.substr(5), values[name], values);
4682                     }else{
4683                         if(args){
4684                             // quoted values are required for strings in compiled templates, 
4685                             // but for non compiled we need to strip them
4686                             // quoted reversed for jsmin
4687                             var re = /^\s*['"](.*)["']\s*$/;
4688                             args = args.split(',');
4689                             for(var i = 0, len = args.length; i < len; i++){
4690                                 args[i] = args[i].replace(re, "$1");
4691                             }
4692                             args = [values[name]].concat(args);
4693                         }else{
4694                             args = [values[name]];
4695                         }
4696                         return fm[format].apply(fm, args);
4697                     }
4698                 }else{
4699                     return values[name] !== undefined ? values[name] : "";
4700                 }
4701             };
4702             return this.html.replace(this.re, fn);
4703         } catch (e) {
4704             Roo.log(e);
4705             throw e;
4706         }
4707          
4708     },
4709     
4710     loading : false,
4711       
4712     load : function ()
4713     {
4714          
4715         if (this.loading) {
4716             return;
4717         }
4718         var _t = this;
4719         
4720         this.loading = true;
4721         this.compiled = false;
4722         
4723         var cx = new Roo.data.Connection();
4724         cx.request({
4725             url : this.url,
4726             method : 'GET',
4727             success : function (response) {
4728                 _t.loading = false;
4729                 _t.html = response.responseText;
4730                 _t.url = false;
4731                 _t.compile();
4732              },
4733             failure : function(response) {
4734                 Roo.log("Template failed to load from " + _t.url);
4735                 _t.loading = false;
4736             }
4737         });
4738     },
4739
4740     /**
4741      * Sets the HTML used as the template and optionally compiles it.
4742      * @param {String} html
4743      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4744      * @return {Roo.Template} this
4745      */
4746     set : function(html, compile){
4747         this.html = html;
4748         this.compiled = null;
4749         if(compile){
4750             this.compile();
4751         }
4752         return this;
4753     },
4754     
4755     /**
4756      * True to disable format functions (defaults to false)
4757      * @type Boolean
4758      */
4759     disableFormats : false,
4760     
4761     /**
4762     * The regular expression used to match template variables 
4763     * @type RegExp
4764     * @property 
4765     */
4766     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4767     
4768     /**
4769      * Compiles the template into an internal function, eliminating the RegEx overhead.
4770      * @return {Roo.Template} this
4771      */
4772     compile : function(){
4773         var fm = Roo.util.Format;
4774         var useF = this.disableFormats !== true;
4775         var sep = Roo.isGecko ? "+" : ",";
4776         var fn = function(m, name, format, args){
4777             if(format && useF){
4778                 args = args ? ',' + args : "";
4779                 if(format.substr(0, 5) != "this."){
4780                     format = "fm." + format + '(';
4781                 }else{
4782                     format = 'this.call("'+ format.substr(5) + '", ';
4783                     args = ", values";
4784                 }
4785             }else{
4786                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4787             }
4788             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4789         };
4790         var body;
4791         // branched to use + in gecko and [].join() in others
4792         if(Roo.isGecko){
4793             body = "this.compiled = function(values){ return '" +
4794                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4795                     "';};";
4796         }else{
4797             body = ["this.compiled = function(values){ return ['"];
4798             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4799             body.push("'].join('');};");
4800             body = body.join('');
4801         }
4802         /**
4803          * eval:var:values
4804          * eval:var:fm
4805          */
4806         eval(body);
4807         return this;
4808     },
4809     
4810     // private function used to call members
4811     call : function(fnName, value, allValues){
4812         return this[fnName](value, allValues);
4813     },
4814     
4815     /**
4816      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4817      * @param {String/HTMLElement/Roo.Element} el The context element
4818      * @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'})
4819      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4820      * @return {HTMLElement/Roo.Element} The new node or Element
4821      */
4822     insertFirst: function(el, values, returnElement){
4823         return this.doInsert('afterBegin', el, values, returnElement);
4824     },
4825
4826     /**
4827      * Applies the supplied values to the template and inserts the new node(s) before el.
4828      * @param {String/HTMLElement/Roo.Element} el The context element
4829      * @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'})
4830      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4831      * @return {HTMLElement/Roo.Element} The new node or Element
4832      */
4833     insertBefore: function(el, values, returnElement){
4834         return this.doInsert('beforeBegin', el, values, returnElement);
4835     },
4836
4837     /**
4838      * Applies the supplied values to the template and inserts the new node(s) after el.
4839      * @param {String/HTMLElement/Roo.Element} el The context element
4840      * @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'})
4841      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4842      * @return {HTMLElement/Roo.Element} The new node or Element
4843      */
4844     insertAfter : function(el, values, returnElement){
4845         return this.doInsert('afterEnd', el, values, returnElement);
4846     },
4847     
4848     /**
4849      * Applies the supplied values to the template and appends the new node(s) to el.
4850      * @param {String/HTMLElement/Roo.Element} el The context element
4851      * @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'})
4852      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4853      * @return {HTMLElement/Roo.Element} The new node or Element
4854      */
4855     append : function(el, values, returnElement){
4856         return this.doInsert('beforeEnd', el, values, returnElement);
4857     },
4858
4859     doInsert : function(where, el, values, returnEl){
4860         el = Roo.getDom(el);
4861         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4862         return returnEl ? Roo.get(newNode, true) : newNode;
4863     },
4864
4865     /**
4866      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4867      * @param {String/HTMLElement/Roo.Element} el The context element
4868      * @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'})
4869      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4870      * @return {HTMLElement/Roo.Element} The new node or Element
4871      */
4872     overwrite : function(el, values, returnElement){
4873         el = Roo.getDom(el);
4874         el.innerHTML = this.applyTemplate(values);
4875         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4876     }
4877 };
4878 /**
4879  * Alias for {@link #applyTemplate}
4880  * @method
4881  */
4882 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4883
4884 // backwards compat
4885 Roo.DomHelper.Template = Roo.Template;
4886
4887 /**
4888  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4889  * @param {String/HTMLElement} el A DOM element or its id
4890  * @returns {Roo.Template} The created template
4891  * @static
4892  */
4893 Roo.Template.from = function(el){
4894     el = Roo.getDom(el);
4895     return new Roo.Template(el.value || el.innerHTML);
4896 };/*
4897  * Based on:
4898  * Ext JS Library 1.1.1
4899  * Copyright(c) 2006-2007, Ext JS, LLC.
4900  *
4901  * Originally Released Under LGPL - original licence link has changed is not relivant.
4902  *
4903  * Fork - LGPL
4904  * <script type="text/javascript">
4905  */
4906  
4907
4908 /*
4909  * This is code is also distributed under MIT license for use
4910  * with jQuery and prototype JavaScript libraries.
4911  */
4912 /**
4913  * @class Roo.DomQuery
4914 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).
4915 <p>
4916 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>
4917
4918 <p>
4919 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.
4920 </p>
4921 <h4>Element Selectors:</h4>
4922 <ul class="list">
4923     <li> <b>*</b> any element</li>
4924     <li> <b>E</b> an element with the tag E</li>
4925     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4926     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4927     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4928     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4929 </ul>
4930 <h4>Attribute Selectors:</h4>
4931 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4932 <ul class="list">
4933     <li> <b>E[foo]</b> has an attribute "foo"</li>
4934     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4935     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4936     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4937     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4938     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4939     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4940 </ul>
4941 <h4>Pseudo Classes:</h4>
4942 <ul class="list">
4943     <li> <b>E:first-child</b> E is the first child of its parent</li>
4944     <li> <b>E:last-child</b> E is the last child of its parent</li>
4945     <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>
4946     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4947     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4948     <li> <b>E:only-child</b> E is the only child of its parent</li>
4949     <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>
4950     <li> <b>E:first</b> the first E in the resultset</li>
4951     <li> <b>E:last</b> the last E in the resultset</li>
4952     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4953     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4954     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4955     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4956     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4957     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4958     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4959     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4960     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4961 </ul>
4962 <h4>CSS Value Selectors:</h4>
4963 <ul class="list">
4964     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4965     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4966     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4967     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4968     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4969     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4970 </ul>
4971  * @singleton
4972  */
4973 Roo.DomQuery = function(){
4974     var cache = {}, simpleCache = {}, valueCache = {};
4975     var nonSpace = /\S/;
4976     var trimRe = /^\s+|\s+$/g;
4977     var tplRe = /\{(\d+)\}/g;
4978     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4979     var tagTokenRe = /^(#)?([\w-\*]+)/;
4980     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4981
4982     function child(p, index){
4983         var i = 0;
4984         var n = p.firstChild;
4985         while(n){
4986             if(n.nodeType == 1){
4987                if(++i == index){
4988                    return n;
4989                }
4990             }
4991             n = n.nextSibling;
4992         }
4993         return null;
4994     };
4995
4996     function next(n){
4997         while((n = n.nextSibling) && n.nodeType != 1);
4998         return n;
4999     };
5000
5001     function prev(n){
5002         while((n = n.previousSibling) && n.nodeType != 1);
5003         return n;
5004     };
5005
5006     function children(d){
5007         var n = d.firstChild, ni = -1;
5008             while(n){
5009                 var nx = n.nextSibling;
5010                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5011                     d.removeChild(n);
5012                 }else{
5013                     n.nodeIndex = ++ni;
5014                 }
5015                 n = nx;
5016             }
5017             return this;
5018         };
5019
5020     function byClassName(c, a, v){
5021         if(!v){
5022             return c;
5023         }
5024         var r = [], ri = -1, cn;
5025         for(var i = 0, ci; ci = c[i]; i++){
5026             if((' '+ci.className+' ').indexOf(v) != -1){
5027                 r[++ri] = ci;
5028             }
5029         }
5030         return r;
5031     };
5032
5033     function attrValue(n, attr){
5034         if(!n.tagName && typeof n.length != "undefined"){
5035             n = n[0];
5036         }
5037         if(!n){
5038             return null;
5039         }
5040         if(attr == "for"){
5041             return n.htmlFor;
5042         }
5043         if(attr == "class" || attr == "className"){
5044             return n.className;
5045         }
5046         return n.getAttribute(attr) || n[attr];
5047
5048     };
5049
5050     function getNodes(ns, mode, tagName){
5051         var result = [], ri = -1, cs;
5052         if(!ns){
5053             return result;
5054         }
5055         tagName = tagName || "*";
5056         if(typeof ns.getElementsByTagName != "undefined"){
5057             ns = [ns];
5058         }
5059         if(!mode){
5060             for(var i = 0, ni; ni = ns[i]; i++){
5061                 cs = ni.getElementsByTagName(tagName);
5062                 for(var j = 0, ci; ci = cs[j]; j++){
5063                     result[++ri] = ci;
5064                 }
5065             }
5066         }else if(mode == "/" || mode == ">"){
5067             var utag = tagName.toUpperCase();
5068             for(var i = 0, ni, cn; ni = ns[i]; i++){
5069                 cn = ni.children || ni.childNodes;
5070                 for(var j = 0, cj; cj = cn[j]; j++){
5071                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5072                         result[++ri] = cj;
5073                     }
5074                 }
5075             }
5076         }else if(mode == "+"){
5077             var utag = tagName.toUpperCase();
5078             for(var i = 0, n; n = ns[i]; i++){
5079                 while((n = n.nextSibling) && n.nodeType != 1);
5080                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5081                     result[++ri] = n;
5082                 }
5083             }
5084         }else if(mode == "~"){
5085             for(var i = 0, n; n = ns[i]; i++){
5086                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5087                 if(n){
5088                     result[++ri] = n;
5089                 }
5090             }
5091         }
5092         return result;
5093     };
5094
5095     function concat(a, b){
5096         if(b.slice){
5097             return a.concat(b);
5098         }
5099         for(var i = 0, l = b.length; i < l; i++){
5100             a[a.length] = b[i];
5101         }
5102         return a;
5103     }
5104
5105     function byTag(cs, tagName){
5106         if(cs.tagName || cs == document){
5107             cs = [cs];
5108         }
5109         if(!tagName){
5110             return cs;
5111         }
5112         var r = [], ri = -1;
5113         tagName = tagName.toLowerCase();
5114         for(var i = 0, ci; ci = cs[i]; i++){
5115             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5116                 r[++ri] = ci;
5117             }
5118         }
5119         return r;
5120     };
5121
5122     function byId(cs, attr, id){
5123         if(cs.tagName || cs == document){
5124             cs = [cs];
5125         }
5126         if(!id){
5127             return cs;
5128         }
5129         var r = [], ri = -1;
5130         for(var i = 0,ci; ci = cs[i]; i++){
5131             if(ci && ci.id == id){
5132                 r[++ri] = ci;
5133                 return r;
5134             }
5135         }
5136         return r;
5137     };
5138
5139     function byAttribute(cs, attr, value, op, custom){
5140         var r = [], ri = -1, st = custom=="{";
5141         var f = Roo.DomQuery.operators[op];
5142         for(var i = 0, ci; ci = cs[i]; i++){
5143             var a;
5144             if(st){
5145                 a = Roo.DomQuery.getStyle(ci, attr);
5146             }
5147             else if(attr == "class" || attr == "className"){
5148                 a = ci.className;
5149             }else if(attr == "for"){
5150                 a = ci.htmlFor;
5151             }else if(attr == "href"){
5152                 a = ci.getAttribute("href", 2);
5153             }else{
5154                 a = ci.getAttribute(attr);
5155             }
5156             if((f && f(a, value)) || (!f && a)){
5157                 r[++ri] = ci;
5158             }
5159         }
5160         return r;
5161     };
5162
5163     function byPseudo(cs, name, value){
5164         return Roo.DomQuery.pseudos[name](cs, value);
5165     };
5166
5167     // This is for IE MSXML which does not support expandos.
5168     // IE runs the same speed using setAttribute, however FF slows way down
5169     // and Safari completely fails so they need to continue to use expandos.
5170     var isIE = window.ActiveXObject ? true : false;
5171
5172     // this eval is stop the compressor from
5173     // renaming the variable to something shorter
5174     
5175     /** eval:var:batch */
5176     var batch = 30803; 
5177
5178     var key = 30803;
5179
5180     function nodupIEXml(cs){
5181         var d = ++key;
5182         cs[0].setAttribute("_nodup", d);
5183         var r = [cs[0]];
5184         for(var i = 1, len = cs.length; i < len; i++){
5185             var c = cs[i];
5186             if(!c.getAttribute("_nodup") != d){
5187                 c.setAttribute("_nodup", d);
5188                 r[r.length] = c;
5189             }
5190         }
5191         for(var i = 0, len = cs.length; i < len; i++){
5192             cs[i].removeAttribute("_nodup");
5193         }
5194         return r;
5195     }
5196
5197     function nodup(cs){
5198         if(!cs){
5199             return [];
5200         }
5201         var len = cs.length, c, i, r = cs, cj, ri = -1;
5202         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5203             return cs;
5204         }
5205         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5206             return nodupIEXml(cs);
5207         }
5208         var d = ++key;
5209         cs[0]._nodup = d;
5210         for(i = 1; c = cs[i]; i++){
5211             if(c._nodup != d){
5212                 c._nodup = d;
5213             }else{
5214                 r = [];
5215                 for(var j = 0; j < i; j++){
5216                     r[++ri] = cs[j];
5217                 }
5218                 for(j = i+1; cj = cs[j]; j++){
5219                     if(cj._nodup != d){
5220                         cj._nodup = d;
5221                         r[++ri] = cj;
5222                     }
5223                 }
5224                 return r;
5225             }
5226         }
5227         return r;
5228     }
5229
5230     function quickDiffIEXml(c1, c2){
5231         var d = ++key;
5232         for(var i = 0, len = c1.length; i < len; i++){
5233             c1[i].setAttribute("_qdiff", d);
5234         }
5235         var r = [];
5236         for(var i = 0, len = c2.length; i < len; i++){
5237             if(c2[i].getAttribute("_qdiff") != d){
5238                 r[r.length] = c2[i];
5239             }
5240         }
5241         for(var i = 0, len = c1.length; i < len; i++){
5242            c1[i].removeAttribute("_qdiff");
5243         }
5244         return r;
5245     }
5246
5247     function quickDiff(c1, c2){
5248         var len1 = c1.length;
5249         if(!len1){
5250             return c2;
5251         }
5252         if(isIE && c1[0].selectSingleNode){
5253             return quickDiffIEXml(c1, c2);
5254         }
5255         var d = ++key;
5256         for(var i = 0; i < len1; i++){
5257             c1[i]._qdiff = d;
5258         }
5259         var r = [];
5260         for(var i = 0, len = c2.length; i < len; i++){
5261             if(c2[i]._qdiff != d){
5262                 r[r.length] = c2[i];
5263             }
5264         }
5265         return r;
5266     }
5267
5268     function quickId(ns, mode, root, id){
5269         if(ns == root){
5270            var d = root.ownerDocument || root;
5271            return d.getElementById(id);
5272         }
5273         ns = getNodes(ns, mode, "*");
5274         return byId(ns, null, id);
5275     }
5276
5277     return {
5278         getStyle : function(el, name){
5279             return Roo.fly(el).getStyle(name);
5280         },
5281         /**
5282          * Compiles a selector/xpath query into a reusable function. The returned function
5283          * takes one parameter "root" (optional), which is the context node from where the query should start.
5284          * @param {String} selector The selector/xpath query
5285          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5286          * @return {Function}
5287          */
5288         compile : function(path, type){
5289             type = type || "select";
5290             
5291             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5292             var q = path, mode, lq;
5293             var tk = Roo.DomQuery.matchers;
5294             var tklen = tk.length;
5295             var mm;
5296
5297             // accept leading mode switch
5298             var lmode = q.match(modeRe);
5299             if(lmode && lmode[1]){
5300                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5301                 q = q.replace(lmode[1], "");
5302             }
5303             // strip leading slashes
5304             while(path.substr(0, 1)=="/"){
5305                 path = path.substr(1);
5306             }
5307
5308             while(q && lq != q){
5309                 lq = q;
5310                 var tm = q.match(tagTokenRe);
5311                 if(type == "select"){
5312                     if(tm){
5313                         if(tm[1] == "#"){
5314                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5315                         }else{
5316                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5317                         }
5318                         q = q.replace(tm[0], "");
5319                     }else if(q.substr(0, 1) != '@'){
5320                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5321                     }
5322                 }else{
5323                     if(tm){
5324                         if(tm[1] == "#"){
5325                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5326                         }else{
5327                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5328                         }
5329                         q = q.replace(tm[0], "");
5330                     }
5331                 }
5332                 while(!(mm = q.match(modeRe))){
5333                     var matched = false;
5334                     for(var j = 0; j < tklen; j++){
5335                         var t = tk[j];
5336                         var m = q.match(t.re);
5337                         if(m){
5338                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5339                                                     return m[i];
5340                                                 });
5341                             q = q.replace(m[0], "");
5342                             matched = true;
5343                             break;
5344                         }
5345                     }
5346                     // prevent infinite loop on bad selector
5347                     if(!matched){
5348                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5349                     }
5350                 }
5351                 if(mm[1]){
5352                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5353                     q = q.replace(mm[1], "");
5354                 }
5355             }
5356             fn[fn.length] = "return nodup(n);\n}";
5357             
5358              /** 
5359               * list of variables that need from compression as they are used by eval.
5360              *  eval:var:batch 
5361              *  eval:var:nodup
5362              *  eval:var:byTag
5363              *  eval:var:ById
5364              *  eval:var:getNodes
5365              *  eval:var:quickId
5366              *  eval:var:mode
5367              *  eval:var:root
5368              *  eval:var:n
5369              *  eval:var:byClassName
5370              *  eval:var:byPseudo
5371              *  eval:var:byAttribute
5372              *  eval:var:attrValue
5373              * 
5374              **/ 
5375             eval(fn.join(""));
5376             return f;
5377         },
5378
5379         /**
5380          * Selects a group of elements.
5381          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5382          * @param {Node} root (optional) The start of the query (defaults to document).
5383          * @return {Array}
5384          */
5385         select : function(path, root, type){
5386             if(!root || root == document){
5387                 root = document;
5388             }
5389             if(typeof root == "string"){
5390                 root = document.getElementById(root);
5391             }
5392             var paths = path.split(",");
5393             var results = [];
5394             for(var i = 0, len = paths.length; i < len; i++){
5395                 var p = paths[i].replace(trimRe, "");
5396                 if(!cache[p]){
5397                     cache[p] = Roo.DomQuery.compile(p);
5398                     if(!cache[p]){
5399                         throw p + " is not a valid selector";
5400                     }
5401                 }
5402                 var result = cache[p](root);
5403                 if(result && result != document){
5404                     results = results.concat(result);
5405                 }
5406             }
5407             if(paths.length > 1){
5408                 return nodup(results);
5409             }
5410             return results;
5411         },
5412
5413         /**
5414          * Selects a single element.
5415          * @param {String} selector The selector/xpath query
5416          * @param {Node} root (optional) The start of the query (defaults to document).
5417          * @return {Element}
5418          */
5419         selectNode : function(path, root){
5420             return Roo.DomQuery.select(path, root)[0];
5421         },
5422
5423         /**
5424          * Selects the value of a node, optionally replacing null with the defaultValue.
5425          * @param {String} selector The selector/xpath query
5426          * @param {Node} root (optional) The start of the query (defaults to document).
5427          * @param {String} defaultValue
5428          */
5429         selectValue : function(path, root, defaultValue){
5430             path = path.replace(trimRe, "");
5431             if(!valueCache[path]){
5432                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5433             }
5434             var n = valueCache[path](root);
5435             n = n[0] ? n[0] : n;
5436             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5437             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5438         },
5439
5440         /**
5441          * Selects the value of a node, parsing integers and floats.
5442          * @param {String} selector The selector/xpath query
5443          * @param {Node} root (optional) The start of the query (defaults to document).
5444          * @param {Number} defaultValue
5445          * @return {Number}
5446          */
5447         selectNumber : function(path, root, defaultValue){
5448             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5449             return parseFloat(v);
5450         },
5451
5452         /**
5453          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5454          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5455          * @param {String} selector The simple selector to test
5456          * @return {Boolean}
5457          */
5458         is : function(el, ss){
5459             if(typeof el == "string"){
5460                 el = document.getElementById(el);
5461             }
5462             var isArray = (el instanceof Array);
5463             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5464             return isArray ? (result.length == el.length) : (result.length > 0);
5465         },
5466
5467         /**
5468          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5469          * @param {Array} el An array of elements to filter
5470          * @param {String} selector The simple selector to test
5471          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5472          * the selector instead of the ones that match
5473          * @return {Array}
5474          */
5475         filter : function(els, ss, nonMatches){
5476             ss = ss.replace(trimRe, "");
5477             if(!simpleCache[ss]){
5478                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5479             }
5480             var result = simpleCache[ss](els);
5481             return nonMatches ? quickDiff(result, els) : result;
5482         },
5483
5484         /**
5485          * Collection of matching regular expressions and code snippets.
5486          */
5487         matchers : [{
5488                 re: /^\.([\w-]+)/,
5489                 select: 'n = byClassName(n, null, " {1} ");'
5490             }, {
5491                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5492                 select: 'n = byPseudo(n, "{1}", "{2}");'
5493             },{
5494                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5495                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5496             }, {
5497                 re: /^#([\w-]+)/,
5498                 select: 'n = byId(n, null, "{1}");'
5499             },{
5500                 re: /^@([\w-]+)/,
5501                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5502             }
5503         ],
5504
5505         /**
5506          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5507          * 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;.
5508          */
5509         operators : {
5510             "=" : function(a, v){
5511                 return a == v;
5512             },
5513             "!=" : function(a, v){
5514                 return a != v;
5515             },
5516             "^=" : function(a, v){
5517                 return a && a.substr(0, v.length) == v;
5518             },
5519             "$=" : function(a, v){
5520                 return a && a.substr(a.length-v.length) == v;
5521             },
5522             "*=" : function(a, v){
5523                 return a && a.indexOf(v) !== -1;
5524             },
5525             "%=" : function(a, v){
5526                 return (a % v) == 0;
5527             },
5528             "|=" : function(a, v){
5529                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5530             },
5531             "~=" : function(a, v){
5532                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5533             }
5534         },
5535
5536         /**
5537          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5538          * and the argument (if any) supplied in the selector.
5539          */
5540         pseudos : {
5541             "first-child" : function(c){
5542                 var r = [], ri = -1, n;
5543                 for(var i = 0, ci; ci = n = c[i]; i++){
5544                     while((n = n.previousSibling) && n.nodeType != 1);
5545                     if(!n){
5546                         r[++ri] = ci;
5547                     }
5548                 }
5549                 return r;
5550             },
5551
5552             "last-child" : function(c){
5553                 var r = [], ri = -1, n;
5554                 for(var i = 0, ci; ci = n = c[i]; i++){
5555                     while((n = n.nextSibling) && n.nodeType != 1);
5556                     if(!n){
5557                         r[++ri] = ci;
5558                     }
5559                 }
5560                 return r;
5561             },
5562
5563             "nth-child" : function(c, a) {
5564                 var r = [], ri = -1;
5565                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5566                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5567                 for(var i = 0, n; n = c[i]; i++){
5568                     var pn = n.parentNode;
5569                     if (batch != pn._batch) {
5570                         var j = 0;
5571                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5572                             if(cn.nodeType == 1){
5573                                cn.nodeIndex = ++j;
5574                             }
5575                         }
5576                         pn._batch = batch;
5577                     }
5578                     if (f == 1) {
5579                         if (l == 0 || n.nodeIndex == l){
5580                             r[++ri] = n;
5581                         }
5582                     } else if ((n.nodeIndex + l) % f == 0){
5583                         r[++ri] = n;
5584                     }
5585                 }
5586
5587                 return r;
5588             },
5589
5590             "only-child" : function(c){
5591                 var r = [], ri = -1;;
5592                 for(var i = 0, ci; ci = c[i]; i++){
5593                     if(!prev(ci) && !next(ci)){
5594                         r[++ri] = ci;
5595                     }
5596                 }
5597                 return r;
5598             },
5599
5600             "empty" : function(c){
5601                 var r = [], ri = -1;
5602                 for(var i = 0, ci; ci = c[i]; i++){
5603                     var cns = ci.childNodes, j = 0, cn, empty = true;
5604                     while(cn = cns[j]){
5605                         ++j;
5606                         if(cn.nodeType == 1 || cn.nodeType == 3){
5607                             empty = false;
5608                             break;
5609                         }
5610                     }
5611                     if(empty){
5612                         r[++ri] = ci;
5613                     }
5614                 }
5615                 return r;
5616             },
5617
5618             "contains" : function(c, v){
5619                 var r = [], ri = -1;
5620                 for(var i = 0, ci; ci = c[i]; i++){
5621                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "nodeValue" : function(c, v){
5629                 var r = [], ri = -1;
5630                 for(var i = 0, ci; ci = c[i]; i++){
5631                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5632                         r[++ri] = ci;
5633                     }
5634                 }
5635                 return r;
5636             },
5637
5638             "checked" : function(c){
5639                 var r = [], ri = -1;
5640                 for(var i = 0, ci; ci = c[i]; i++){
5641                     if(ci.checked == true){
5642                         r[++ri] = ci;
5643                     }
5644                 }
5645                 return r;
5646             },
5647
5648             "not" : function(c, ss){
5649                 return Roo.DomQuery.filter(c, ss, true);
5650             },
5651
5652             "odd" : function(c){
5653                 return this["nth-child"](c, "odd");
5654             },
5655
5656             "even" : function(c){
5657                 return this["nth-child"](c, "even");
5658             },
5659
5660             "nth" : function(c, a){
5661                 return c[a-1] || [];
5662             },
5663
5664             "first" : function(c){
5665                 return c[0] || [];
5666             },
5667
5668             "last" : function(c){
5669                 return c[c.length-1] || [];
5670             },
5671
5672             "has" : function(c, ss){
5673                 var s = Roo.DomQuery.select;
5674                 var r = [], ri = -1;
5675                 for(var i = 0, ci; ci = c[i]; i++){
5676                     if(s(ss, ci).length > 0){
5677                         r[++ri] = ci;
5678                     }
5679                 }
5680                 return r;
5681             },
5682
5683             "next" : function(c, ss){
5684                 var is = Roo.DomQuery.is;
5685                 var r = [], ri = -1;
5686                 for(var i = 0, ci; ci = c[i]; i++){
5687                     var n = next(ci);
5688                     if(n && is(n, ss)){
5689                         r[++ri] = ci;
5690                     }
5691                 }
5692                 return r;
5693             },
5694
5695             "prev" : function(c, ss){
5696                 var is = Roo.DomQuery.is;
5697                 var r = [], ri = -1;
5698                 for(var i = 0, ci; ci = c[i]; i++){
5699                     var n = prev(ci);
5700                     if(n && is(n, ss)){
5701                         r[++ri] = ci;
5702                     }
5703                 }
5704                 return r;
5705             }
5706         }
5707     };
5708 }();
5709
5710 /**
5711  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5712  * @param {String} path The selector/xpath query
5713  * @param {Node} root (optional) The start of the query (defaults to document).
5714  * @return {Array}
5715  * @member Roo
5716  * @method query
5717  */
5718 Roo.query = Roo.DomQuery.select;
5719 /*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729
5730 /**
5731  * @class Roo.util.Observable
5732  * Base class that provides a common interface for publishing events. Subclasses are expected to
5733  * to have a property "events" with all the events defined.<br>
5734  * For example:
5735  * <pre><code>
5736  Employee = function(name){
5737     this.name = name;
5738     this.addEvents({
5739         "fired" : true,
5740         "quit" : true
5741     });
5742  }
5743  Roo.extend(Employee, Roo.util.Observable);
5744 </code></pre>
5745  * @param {Object} config properties to use (incuding events / listeners)
5746  */
5747
5748 Roo.util.Observable = function(cfg){
5749     
5750     cfg = cfg|| {};
5751     this.addEvents(cfg.events || {});
5752     if (cfg.events) {
5753         delete cfg.events; // make sure
5754     }
5755      
5756     Roo.apply(this, cfg);
5757     
5758     if(this.listeners){
5759         this.on(this.listeners);
5760         delete this.listeners;
5761     }
5762 };
5763 Roo.util.Observable.prototype = {
5764     /** 
5765  * @cfg {Object} listeners  list of events and functions to call for this object, 
5766  * For example :
5767  * <pre><code>
5768     listeners :  { 
5769        'click' : function(e) {
5770            ..... 
5771         } ,
5772         .... 
5773     } 
5774   </code></pre>
5775  */
5776     
5777     
5778     /**
5779      * Fires the specified event with the passed parameters (minus the event name).
5780      * @param {String} eventName
5781      * @param {Object...} args Variable number of parameters are passed to handlers
5782      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5783      */
5784     fireEvent : function(){
5785         var ce = this.events[arguments[0].toLowerCase()];
5786         if(typeof ce == "object"){
5787             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5788         }else{
5789             return true;
5790         }
5791     },
5792
5793     // private
5794     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5795
5796     /**
5797      * Appends an event handler to this component
5798      * @param {String}   eventName The type of event to listen for
5799      * @param {Function} handler The method the event invokes
5800      * @param {Object}   scope (optional) The scope in which to execute the handler
5801      * function. The handler function's "this" context.
5802      * @param {Object}   options (optional) An object containing handler configuration
5803      * properties. This may contain any of the following properties:<ul>
5804      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5805      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5806      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5807      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5808      * by the specified number of milliseconds. If the event fires again within that time, the original
5809      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5810      * </ul><br>
5811      * <p>
5812      * <b>Combining Options</b><br>
5813      * Using the options argument, it is possible to combine different types of listeners:<br>
5814      * <br>
5815      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5816                 <pre><code>
5817                 el.on('click', this.onClick, this, {
5818                         single: true,
5819                 delay: 100,
5820                 forumId: 4
5821                 });
5822                 </code></pre>
5823      * <p>
5824      * <b>Attaching multiple handlers in 1 call</b><br>
5825      * The method also allows for a single argument to be passed which is a config object containing properties
5826      * which specify multiple handlers.
5827      * <pre><code>
5828                 el.on({
5829                         'click': {
5830                         fn: this.onClick,
5831                         scope: this,
5832                         delay: 100
5833                 }, 
5834                 'mouseover': {
5835                         fn: this.onMouseOver,
5836                         scope: this
5837                 },
5838                 'mouseout': {
5839                         fn: this.onMouseOut,
5840                         scope: this
5841                 }
5842                 });
5843                 </code></pre>
5844      * <p>
5845      * Or a shorthand syntax which passes the same scope object to all handlers:
5846         <pre><code>
5847                 el.on({
5848                         'click': this.onClick,
5849                 'mouseover': this.onMouseOver,
5850                 'mouseout': this.onMouseOut,
5851                 scope: this
5852                 });
5853                 </code></pre>
5854      */
5855     addListener : function(eventName, fn, scope, o){
5856         if(typeof eventName == "object"){
5857             o = eventName;
5858             for(var e in o){
5859                 if(this.filterOptRe.test(e)){
5860                     continue;
5861                 }
5862                 if(typeof o[e] == "function"){
5863                     // shared options
5864                     this.addListener(e, o[e], o.scope,  o);
5865                 }else{
5866                     // individual options
5867                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5868                 }
5869             }
5870             return;
5871         }
5872         o = (!o || typeof o == "boolean") ? {} : o;
5873         eventName = eventName.toLowerCase();
5874         var ce = this.events[eventName] || true;
5875         if(typeof ce == "boolean"){
5876             ce = new Roo.util.Event(this, eventName);
5877             this.events[eventName] = ce;
5878         }
5879         ce.addListener(fn, scope, o);
5880     },
5881
5882     /**
5883      * Removes a listener
5884      * @param {String}   eventName     The type of event to listen for
5885      * @param {Function} handler        The handler to remove
5886      * @param {Object}   scope  (optional) The scope (this object) for the handler
5887      */
5888     removeListener : function(eventName, fn, scope){
5889         var ce = this.events[eventName.toLowerCase()];
5890         if(typeof ce == "object"){
5891             ce.removeListener(fn, scope);
5892         }
5893     },
5894
5895     /**
5896      * Removes all listeners for this object
5897      */
5898     purgeListeners : function(){
5899         for(var evt in this.events){
5900             if(typeof this.events[evt] == "object"){
5901                  this.events[evt].clearListeners();
5902             }
5903         }
5904     },
5905
5906     relayEvents : function(o, events){
5907         var createHandler = function(ename){
5908             return function(){
5909                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5910             };
5911         };
5912         for(var i = 0, len = events.length; i < len; i++){
5913             var ename = events[i];
5914             if(!this.events[ename]){ this.events[ename] = true; };
5915             o.on(ename, createHandler(ename), this);
5916         }
5917     },
5918
5919     /**
5920      * Used to define events on this Observable
5921      * @param {Object} object The object with the events defined
5922      */
5923     addEvents : function(o){
5924         if(!this.events){
5925             this.events = {};
5926         }
5927         Roo.applyIf(this.events, o);
5928     },
5929
5930     /**
5931      * Checks to see if this object has any listeners for a specified event
5932      * @param {String} eventName The name of the event to check for
5933      * @return {Boolean} True if the event is being listened for, else false
5934      */
5935     hasListener : function(eventName){
5936         var e = this.events[eventName];
5937         return typeof e == "object" && e.listeners.length > 0;
5938     }
5939 };
5940 /**
5941  * Appends an event handler to this element (shorthand for addListener)
5942  * @param {String}   eventName     The type of event to listen for
5943  * @param {Function} handler        The method the event invokes
5944  * @param {Object}   scope (optional) The scope in which to execute the handler
5945  * function. The handler function's "this" context.
5946  * @param {Object}   options  (optional)
5947  * @method
5948  */
5949 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5950 /**
5951  * Removes a listener (shorthand for removeListener)
5952  * @param {String}   eventName     The type of event to listen for
5953  * @param {Function} handler        The handler to remove
5954  * @param {Object}   scope  (optional) The scope (this object) for the handler
5955  * @method
5956  */
5957 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5958
5959 /**
5960  * Starts capture on the specified Observable. All events will be passed
5961  * to the supplied function with the event name + standard signature of the event
5962  * <b>before</b> the event is fired. If the supplied function returns false,
5963  * the event will not fire.
5964  * @param {Observable} o The Observable to capture
5965  * @param {Function} fn The function to call
5966  * @param {Object} scope (optional) The scope (this object) for the fn
5967  * @static
5968  */
5969 Roo.util.Observable.capture = function(o, fn, scope){
5970     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5971 };
5972
5973 /**
5974  * Removes <b>all</b> added captures from the Observable.
5975  * @param {Observable} o The Observable to release
5976  * @static
5977  */
5978 Roo.util.Observable.releaseCapture = function(o){
5979     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5980 };
5981
5982 (function(){
5983
5984     var createBuffered = function(h, o, scope){
5985         var task = new Roo.util.DelayedTask();
5986         return function(){
5987             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5988         };
5989     };
5990
5991     var createSingle = function(h, e, fn, scope){
5992         return function(){
5993             e.removeListener(fn, scope);
5994             return h.apply(scope, arguments);
5995         };
5996     };
5997
5998     var createDelayed = function(h, o, scope){
5999         return function(){
6000             var args = Array.prototype.slice.call(arguments, 0);
6001             setTimeout(function(){
6002                 h.apply(scope, args);
6003             }, o.delay || 10);
6004         };
6005     };
6006
6007     Roo.util.Event = function(obj, name){
6008         this.name = name;
6009         this.obj = obj;
6010         this.listeners = [];
6011     };
6012
6013     Roo.util.Event.prototype = {
6014         addListener : function(fn, scope, options){
6015             var o = options || {};
6016             scope = scope || this.obj;
6017             if(!this.isListening(fn, scope)){
6018                 var l = {fn: fn, scope: scope, options: o};
6019                 var h = fn;
6020                 if(o.delay){
6021                     h = createDelayed(h, o, scope);
6022                 }
6023                 if(o.single){
6024                     h = createSingle(h, this, fn, scope);
6025                 }
6026                 if(o.buffer){
6027                     h = createBuffered(h, o, scope);
6028                 }
6029                 l.fireFn = h;
6030                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6031                     this.listeners.push(l);
6032                 }else{
6033                     this.listeners = this.listeners.slice(0);
6034                     this.listeners.push(l);
6035                 }
6036             }
6037         },
6038
6039         findListener : function(fn, scope){
6040             scope = scope || this.obj;
6041             var ls = this.listeners;
6042             for(var i = 0, len = ls.length; i < len; i++){
6043                 var l = ls[i];
6044                 if(l.fn == fn && l.scope == scope){
6045                     return i;
6046                 }
6047             }
6048             return -1;
6049         },
6050
6051         isListening : function(fn, scope){
6052             return this.findListener(fn, scope) != -1;
6053         },
6054
6055         removeListener : function(fn, scope){
6056             var index;
6057             if((index = this.findListener(fn, scope)) != -1){
6058                 if(!this.firing){
6059                     this.listeners.splice(index, 1);
6060                 }else{
6061                     this.listeners = this.listeners.slice(0);
6062                     this.listeners.splice(index, 1);
6063                 }
6064                 return true;
6065             }
6066             return false;
6067         },
6068
6069         clearListeners : function(){
6070             this.listeners = [];
6071         },
6072
6073         fire : function(){
6074             var ls = this.listeners, scope, len = ls.length;
6075             if(len > 0){
6076                 this.firing = true;
6077                 
6078                 for(var i = 0; i < len; i++){
6079                     var args = Array.prototype.slice.call(arguments, 0);
6080                     var l = ls[i];
6081                     args.push(l.options);
6082                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6083                         this.firing = false;
6084                         return false;
6085                     }
6086                 }
6087                 this.firing = false;
6088             }
6089             return true;
6090         }
6091     };
6092 })();/*
6093  * RooJS Library 
6094  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6095  *
6096  * Licence LGPL 
6097  *
6098  */
6099  
6100 /**
6101  * @class Roo.Document
6102  * @extends Roo.util.Observable
6103  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6104  * 
6105  * @param {Object} config the methods and properties of the 'base' class for the application.
6106  * 
6107  *  Generic Page handler - implement this to start your app..
6108  * 
6109  * eg.
6110  *  MyProject = new Roo.Document({
6111         events : {
6112             'load' : true // your events..
6113         },
6114         listeners : {
6115             'ready' : function() {
6116                 // fired on Roo.onReady()
6117             }
6118         }
6119  * 
6120  */
6121 Roo.Document = function(cfg) {
6122      
6123     this.addEvents({ 
6124         'ready' : true
6125     });
6126     Roo.util.Observable.call(this,cfg);
6127     
6128     var _this = this;
6129     
6130     Roo.onReady(function() {
6131         _this.fireEvent('ready');
6132     },null,false);
6133     
6134     
6135 }
6136
6137 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6138  * Based on:
6139  * Ext JS Library 1.1.1
6140  * Copyright(c) 2006-2007, Ext JS, LLC.
6141  *
6142  * Originally Released Under LGPL - original licence link has changed is not relivant.
6143  *
6144  * Fork - LGPL
6145  * <script type="text/javascript">
6146  */
6147
6148 /**
6149  * @class Roo.EventManager
6150  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6151  * several useful events directly.
6152  * See {@link Roo.EventObject} for more details on normalized event objects.
6153  * @singleton
6154  */
6155 Roo.EventManager = function(){
6156     var docReadyEvent, docReadyProcId, docReadyState = false;
6157     var resizeEvent, resizeTask, textEvent, textSize;
6158     var E = Roo.lib.Event;
6159     var D = Roo.lib.Dom;
6160
6161     
6162     
6163
6164     var fireDocReady = function(){
6165         if(!docReadyState){
6166             docReadyState = true;
6167             Roo.isReady = true;
6168             if(docReadyProcId){
6169                 clearInterval(docReadyProcId);
6170             }
6171             if(Roo.isGecko || Roo.isOpera) {
6172                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6173             }
6174             if(Roo.isIE){
6175                 var defer = document.getElementById("ie-deferred-loader");
6176                 if(defer){
6177                     defer.onreadystatechange = null;
6178                     defer.parentNode.removeChild(defer);
6179                 }
6180             }
6181             if(docReadyEvent){
6182                 docReadyEvent.fire();
6183                 docReadyEvent.clearListeners();
6184             }
6185         }
6186     };
6187     
6188     var initDocReady = function(){
6189         docReadyEvent = new Roo.util.Event();
6190         if(Roo.isGecko || Roo.isOpera) {
6191             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6192         }else if(Roo.isIE){
6193             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6194             var defer = document.getElementById("ie-deferred-loader");
6195             defer.onreadystatechange = function(){
6196                 if(this.readyState == "complete"){
6197                     fireDocReady();
6198                 }
6199             };
6200         }else if(Roo.isSafari){ 
6201             docReadyProcId = setInterval(function(){
6202                 var rs = document.readyState;
6203                 if(rs == "complete") {
6204                     fireDocReady();     
6205                  }
6206             }, 10);
6207         }
6208         // no matter what, make sure it fires on load
6209         E.on(window, "load", fireDocReady);
6210     };
6211
6212     var createBuffered = function(h, o){
6213         var task = new Roo.util.DelayedTask(h);
6214         return function(e){
6215             // create new event object impl so new events don't wipe out properties
6216             e = new Roo.EventObjectImpl(e);
6217             task.delay(o.buffer, h, null, [e]);
6218         };
6219     };
6220
6221     var createSingle = function(h, el, ename, fn){
6222         return function(e){
6223             Roo.EventManager.removeListener(el, ename, fn);
6224             h(e);
6225         };
6226     };
6227
6228     var createDelayed = function(h, o){
6229         return function(e){
6230             // create new event object impl so new events don't wipe out properties
6231             e = new Roo.EventObjectImpl(e);
6232             setTimeout(function(){
6233                 h(e);
6234             }, o.delay || 10);
6235         };
6236     };
6237     var transitionEndVal = false;
6238     
6239     var transitionEnd = function()
6240     {
6241         if (transitionEndVal) {
6242             return transitionEndVal;
6243         }
6244         var el = document.createElement('div');
6245
6246         var transEndEventNames = {
6247             WebkitTransition : 'webkitTransitionEnd',
6248             MozTransition    : 'transitionend',
6249             OTransition      : 'oTransitionEnd otransitionend',
6250             transition       : 'transitionend'
6251         };
6252     
6253         for (var name in transEndEventNames) {
6254             if (el.style[name] !== undefined) {
6255                 transitionEndVal = transEndEventNames[name];
6256                 return  transitionEndVal ;
6257             }
6258         }
6259     }
6260     
6261
6262     var listen = function(element, ename, opt, fn, scope){
6263         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6264         fn = fn || o.fn; scope = scope || o.scope;
6265         var el = Roo.getDom(element);
6266         
6267         
6268         if(!el){
6269             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6270         }
6271         
6272         if (ename == 'transitionend') {
6273             ename = transitionEnd();
6274         }
6275         var h = function(e){
6276             e = Roo.EventObject.setEvent(e);
6277             var t;
6278             if(o.delegate){
6279                 t = e.getTarget(o.delegate, el);
6280                 if(!t){
6281                     return;
6282                 }
6283             }else{
6284                 t = e.target;
6285             }
6286             if(o.stopEvent === true){
6287                 e.stopEvent();
6288             }
6289             if(o.preventDefault === true){
6290                e.preventDefault();
6291             }
6292             if(o.stopPropagation === true){
6293                 e.stopPropagation();
6294             }
6295
6296             if(o.normalized === false){
6297                 e = e.browserEvent;
6298             }
6299
6300             fn.call(scope || el, e, t, o);
6301         };
6302         if(o.delay){
6303             h = createDelayed(h, o);
6304         }
6305         if(o.single){
6306             h = createSingle(h, el, ename, fn);
6307         }
6308         if(o.buffer){
6309             h = createBuffered(h, o);
6310         }
6311         
6312         fn._handlers = fn._handlers || [];
6313         
6314         
6315         fn._handlers.push([Roo.id(el), ename, h]);
6316         
6317         
6318          
6319         E.on(el, ename, h);
6320         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6321             el.addEventListener("DOMMouseScroll", h, false);
6322             E.on(window, 'unload', function(){
6323                 el.removeEventListener("DOMMouseScroll", h, false);
6324             });
6325         }
6326         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6327             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6328         }
6329         return h;
6330     };
6331
6332     var stopListening = function(el, ename, fn){
6333         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6334         if(hds){
6335             for(var i = 0, len = hds.length; i < len; i++){
6336                 var h = hds[i];
6337                 if(h[0] == id && h[1] == ename){
6338                     hd = h[2];
6339                     hds.splice(i, 1);
6340                     break;
6341                 }
6342             }
6343         }
6344         E.un(el, ename, hd);
6345         el = Roo.getDom(el);
6346         if(ename == "mousewheel" && el.addEventListener){
6347             el.removeEventListener("DOMMouseScroll", hd, false);
6348         }
6349         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6350             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6351         }
6352     };
6353
6354     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6355     
6356     var pub = {
6357         
6358         
6359         /** 
6360          * Fix for doc tools
6361          * @scope Roo.EventManager
6362          */
6363         
6364         
6365         /** 
6366          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6367          * object with a Roo.EventObject
6368          * @param {Function} fn        The method the event invokes
6369          * @param {Object}   scope    An object that becomes the scope of the handler
6370          * @param {boolean}  override If true, the obj passed in becomes
6371          *                             the execution scope of the listener
6372          * @return {Function} The wrapped function
6373          * @deprecated
6374          */
6375         wrap : function(fn, scope, override){
6376             return function(e){
6377                 Roo.EventObject.setEvent(e);
6378                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6379             };
6380         },
6381         
6382         /**
6383      * Appends an event handler to an element (shorthand for addListener)
6384      * @param {String/HTMLElement}   element        The html element or id to assign the
6385      * @param {String}   eventName The type of event to listen for
6386      * @param {Function} handler The method the event invokes
6387      * @param {Object}   scope (optional) The scope in which to execute the handler
6388      * function. The handler function's "this" context.
6389      * @param {Object}   options (optional) An object containing handler configuration
6390      * properties. This may contain any of the following properties:<ul>
6391      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6392      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6393      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6394      * <li>preventDefault {Boolean} True to prevent the default action</li>
6395      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6396      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6397      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6398      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6399      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6400      * by the specified number of milliseconds. If the event fires again within that time, the original
6401      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6402      * </ul><br>
6403      * <p>
6404      * <b>Combining Options</b><br>
6405      * Using the options argument, it is possible to combine different types of listeners:<br>
6406      * <br>
6407      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6408      * Code:<pre><code>
6409 el.on('click', this.onClick, this, {
6410     single: true,
6411     delay: 100,
6412     stopEvent : true,
6413     forumId: 4
6414 });</code></pre>
6415      * <p>
6416      * <b>Attaching multiple handlers in 1 call</b><br>
6417       * The method also allows for a single argument to be passed which is a config object containing properties
6418      * which specify multiple handlers.
6419      * <p>
6420      * Code:<pre><code>
6421 el.on({
6422     'click' : {
6423         fn: this.onClick
6424         scope: this,
6425         delay: 100
6426     },
6427     'mouseover' : {
6428         fn: this.onMouseOver
6429         scope: this
6430     },
6431     'mouseout' : {
6432         fn: this.onMouseOut
6433         scope: this
6434     }
6435 });</code></pre>
6436      * <p>
6437      * Or a shorthand syntax:<br>
6438      * Code:<pre><code>
6439 el.on({
6440     'click' : this.onClick,
6441     'mouseover' : this.onMouseOver,
6442     'mouseout' : this.onMouseOut
6443     scope: this
6444 });</code></pre>
6445      */
6446         addListener : function(element, eventName, fn, scope, options){
6447             if(typeof eventName == "object"){
6448                 var o = eventName;
6449                 for(var e in o){
6450                     if(propRe.test(e)){
6451                         continue;
6452                     }
6453                     if(typeof o[e] == "function"){
6454                         // shared options
6455                         listen(element, e, o, o[e], o.scope);
6456                     }else{
6457                         // individual options
6458                         listen(element, e, o[e]);
6459                     }
6460                 }
6461                 return;
6462             }
6463             return listen(element, eventName, options, fn, scope);
6464         },
6465         
6466         /**
6467          * Removes an event handler
6468          *
6469          * @param {String/HTMLElement}   element        The id or html element to remove the 
6470          *                             event from
6471          * @param {String}   eventName     The type of event
6472          * @param {Function} fn
6473          * @return {Boolean} True if a listener was actually removed
6474          */
6475         removeListener : function(element, eventName, fn){
6476             return stopListening(element, eventName, fn);
6477         },
6478         
6479         /**
6480          * Fires when the document is ready (before onload and before images are loaded). Can be 
6481          * accessed shorthanded Roo.onReady().
6482          * @param {Function} fn        The method the event invokes
6483          * @param {Object}   scope    An  object that becomes the scope of the handler
6484          * @param {boolean}  options
6485          */
6486         onDocumentReady : function(fn, scope, options){
6487             if(docReadyState){ // if it already fired
6488                 docReadyEvent.addListener(fn, scope, options);
6489                 docReadyEvent.fire();
6490                 docReadyEvent.clearListeners();
6491                 return;
6492             }
6493             if(!docReadyEvent){
6494                 initDocReady();
6495             }
6496             docReadyEvent.addListener(fn, scope, options);
6497         },
6498         
6499         /**
6500          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6501          * @param {Function} fn        The method the event invokes
6502          * @param {Object}   scope    An object that becomes the scope of the handler
6503          * @param {boolean}  options
6504          */
6505         onWindowResize : function(fn, scope, options){
6506             if(!resizeEvent){
6507                 resizeEvent = new Roo.util.Event();
6508                 resizeTask = new Roo.util.DelayedTask(function(){
6509                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6510                 });
6511                 E.on(window, "resize", function(){
6512                     if(Roo.isIE){
6513                         resizeTask.delay(50);
6514                     }else{
6515                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6516                     }
6517                 });
6518             }
6519             resizeEvent.addListener(fn, scope, options);
6520         },
6521
6522         /**
6523          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6524          * @param {Function} fn        The method the event invokes
6525          * @param {Object}   scope    An object that becomes the scope of the handler
6526          * @param {boolean}  options
6527          */
6528         onTextResize : function(fn, scope, options){
6529             if(!textEvent){
6530                 textEvent = new Roo.util.Event();
6531                 var textEl = new Roo.Element(document.createElement('div'));
6532                 textEl.dom.className = 'x-text-resize';
6533                 textEl.dom.innerHTML = 'X';
6534                 textEl.appendTo(document.body);
6535                 textSize = textEl.dom.offsetHeight;
6536                 setInterval(function(){
6537                     if(textEl.dom.offsetHeight != textSize){
6538                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6539                     }
6540                 }, this.textResizeInterval);
6541             }
6542             textEvent.addListener(fn, scope, options);
6543         },
6544
6545         /**
6546          * Removes the passed window resize listener.
6547          * @param {Function} fn        The method the event invokes
6548          * @param {Object}   scope    The scope of handler
6549          */
6550         removeResizeListener : function(fn, scope){
6551             if(resizeEvent){
6552                 resizeEvent.removeListener(fn, scope);
6553             }
6554         },
6555
6556         // private
6557         fireResize : function(){
6558             if(resizeEvent){
6559                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6560             }   
6561         },
6562         /**
6563          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6564          */
6565         ieDeferSrc : false,
6566         /**
6567          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6568          */
6569         textResizeInterval : 50
6570     };
6571     
6572     /**
6573      * Fix for doc tools
6574      * @scopeAlias pub=Roo.EventManager
6575      */
6576     
6577      /**
6578      * Appends an event handler to an element (shorthand for addListener)
6579      * @param {String/HTMLElement}   element        The html element or id to assign the
6580      * @param {String}   eventName The type of event to listen for
6581      * @param {Function} handler The method the event invokes
6582      * @param {Object}   scope (optional) The scope in which to execute the handler
6583      * function. The handler function's "this" context.
6584      * @param {Object}   options (optional) An object containing handler configuration
6585      * properties. This may contain any of the following properties:<ul>
6586      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6587      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6588      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6589      * <li>preventDefault {Boolean} True to prevent the default action</li>
6590      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6591      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6592      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6593      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6594      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6595      * by the specified number of milliseconds. If the event fires again within that time, the original
6596      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6597      * </ul><br>
6598      * <p>
6599      * <b>Combining Options</b><br>
6600      * Using the options argument, it is possible to combine different types of listeners:<br>
6601      * <br>
6602      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6603      * Code:<pre><code>
6604 el.on('click', this.onClick, this, {
6605     single: true,
6606     delay: 100,
6607     stopEvent : true,
6608     forumId: 4
6609 });</code></pre>
6610      * <p>
6611      * <b>Attaching multiple handlers in 1 call</b><br>
6612       * The method also allows for a single argument to be passed which is a config object containing properties
6613      * which specify multiple handlers.
6614      * <p>
6615      * Code:<pre><code>
6616 el.on({
6617     'click' : {
6618         fn: this.onClick
6619         scope: this,
6620         delay: 100
6621     },
6622     'mouseover' : {
6623         fn: this.onMouseOver
6624         scope: this
6625     },
6626     'mouseout' : {
6627         fn: this.onMouseOut
6628         scope: this
6629     }
6630 });</code></pre>
6631      * <p>
6632      * Or a shorthand syntax:<br>
6633      * Code:<pre><code>
6634 el.on({
6635     'click' : this.onClick,
6636     'mouseover' : this.onMouseOver,
6637     'mouseout' : this.onMouseOut
6638     scope: this
6639 });</code></pre>
6640      */
6641     pub.on = pub.addListener;
6642     pub.un = pub.removeListener;
6643
6644     pub.stoppedMouseDownEvent = new Roo.util.Event();
6645     return pub;
6646 }();
6647 /**
6648   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6649   * @param {Function} fn        The method the event invokes
6650   * @param {Object}   scope    An  object that becomes the scope of the handler
6651   * @param {boolean}  override If true, the obj passed in becomes
6652   *                             the execution scope of the listener
6653   * @member Roo
6654   * @method onReady
6655  */
6656 Roo.onReady = Roo.EventManager.onDocumentReady;
6657
6658 Roo.onReady(function(){
6659     var bd = Roo.get(document.body);
6660     if(!bd){ return; }
6661
6662     var cls = [
6663             Roo.isIE ? "roo-ie"
6664             : Roo.isIE11 ? "roo-ie11"
6665             : Roo.isEdge ? "roo-edge"
6666             : Roo.isGecko ? "roo-gecko"
6667             : Roo.isOpera ? "roo-opera"
6668             : Roo.isSafari ? "roo-safari" : ""];
6669
6670     if(Roo.isMac){
6671         cls.push("roo-mac");
6672     }
6673     if(Roo.isLinux){
6674         cls.push("roo-linux");
6675     }
6676     if(Roo.isIOS){
6677         cls.push("roo-ios");
6678     }
6679     if(Roo.isTouch){
6680         cls.push("roo-touch");
6681     }
6682     if(Roo.isBorderBox){
6683         cls.push('roo-border-box');
6684     }
6685     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6686         var p = bd.dom.parentNode;
6687         if(p){
6688             p.className += ' roo-strict';
6689         }
6690     }
6691     bd.addClass(cls.join(' '));
6692 });
6693
6694 /**
6695  * @class Roo.EventObject
6696  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6697  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6698  * Example:
6699  * <pre><code>
6700  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6701     e.preventDefault();
6702     var target = e.getTarget();
6703     ...
6704  }
6705  var myDiv = Roo.get("myDiv");
6706  myDiv.on("click", handleClick);
6707  //or
6708  Roo.EventManager.on("myDiv", 'click', handleClick);
6709  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6710  </code></pre>
6711  * @singleton
6712  */
6713 Roo.EventObject = function(){
6714     
6715     var E = Roo.lib.Event;
6716     
6717     // safari keypress events for special keys return bad keycodes
6718     var safariKeys = {
6719         63234 : 37, // left
6720         63235 : 39, // right
6721         63232 : 38, // up
6722         63233 : 40, // down
6723         63276 : 33, // page up
6724         63277 : 34, // page down
6725         63272 : 46, // delete
6726         63273 : 36, // home
6727         63275 : 35  // end
6728     };
6729
6730     // normalize button clicks
6731     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6732                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6733
6734     Roo.EventObjectImpl = function(e){
6735         if(e){
6736             this.setEvent(e.browserEvent || e);
6737         }
6738     };
6739     Roo.EventObjectImpl.prototype = {
6740         /**
6741          * Used to fix doc tools.
6742          * @scope Roo.EventObject.prototype
6743          */
6744             
6745
6746         
6747         
6748         /** The normal browser event */
6749         browserEvent : null,
6750         /** The button pressed in a mouse event */
6751         button : -1,
6752         /** True if the shift key was down during the event */
6753         shiftKey : false,
6754         /** True if the control key was down during the event */
6755         ctrlKey : false,
6756         /** True if the alt key was down during the event */
6757         altKey : false,
6758
6759         /** Key constant 
6760         * @type Number */
6761         BACKSPACE : 8,
6762         /** Key constant 
6763         * @type Number */
6764         TAB : 9,
6765         /** Key constant 
6766         * @type Number */
6767         RETURN : 13,
6768         /** Key constant 
6769         * @type Number */
6770         ENTER : 13,
6771         /** Key constant 
6772         * @type Number */
6773         SHIFT : 16,
6774         /** Key constant 
6775         * @type Number */
6776         CONTROL : 17,
6777         /** Key constant 
6778         * @type Number */
6779         ESC : 27,
6780         /** Key constant 
6781         * @type Number */
6782         SPACE : 32,
6783         /** Key constant 
6784         * @type Number */
6785         PAGEUP : 33,
6786         /** Key constant 
6787         * @type Number */
6788         PAGEDOWN : 34,
6789         /** Key constant 
6790         * @type Number */
6791         END : 35,
6792         /** Key constant 
6793         * @type Number */
6794         HOME : 36,
6795         /** Key constant 
6796         * @type Number */
6797         LEFT : 37,
6798         /** Key constant 
6799         * @type Number */
6800         UP : 38,
6801         /** Key constant 
6802         * @type Number */
6803         RIGHT : 39,
6804         /** Key constant 
6805         * @type Number */
6806         DOWN : 40,
6807         /** Key constant 
6808         * @type Number */
6809         DELETE : 46,
6810         /** Key constant 
6811         * @type Number */
6812         F5 : 116,
6813
6814            /** @private */
6815         setEvent : function(e){
6816             if(e == this || (e && e.browserEvent)){ // already wrapped
6817                 return e;
6818             }
6819             this.browserEvent = e;
6820             if(e){
6821                 // normalize buttons
6822                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6823                 if(e.type == 'click' && this.button == -1){
6824                     this.button = 0;
6825                 }
6826                 this.type = e.type;
6827                 this.shiftKey = e.shiftKey;
6828                 // mac metaKey behaves like ctrlKey
6829                 this.ctrlKey = e.ctrlKey || e.metaKey;
6830                 this.altKey = e.altKey;
6831                 // in getKey these will be normalized for the mac
6832                 this.keyCode = e.keyCode;
6833                 // keyup warnings on firefox.
6834                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6835                 // cache the target for the delayed and or buffered events
6836                 this.target = E.getTarget(e);
6837                 // same for XY
6838                 this.xy = E.getXY(e);
6839             }else{
6840                 this.button = -1;
6841                 this.shiftKey = false;
6842                 this.ctrlKey = false;
6843                 this.altKey = false;
6844                 this.keyCode = 0;
6845                 this.charCode =0;
6846                 this.target = null;
6847                 this.xy = [0, 0];
6848             }
6849             return this;
6850         },
6851
6852         /**
6853          * Stop the event (preventDefault and stopPropagation)
6854          */
6855         stopEvent : function(){
6856             if(this.browserEvent){
6857                 if(this.browserEvent.type == 'mousedown'){
6858                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6859                 }
6860                 E.stopEvent(this.browserEvent);
6861             }
6862         },
6863
6864         /**
6865          * Prevents the browsers default handling of the event.
6866          */
6867         preventDefault : function(){
6868             if(this.browserEvent){
6869                 E.preventDefault(this.browserEvent);
6870             }
6871         },
6872
6873         /** @private */
6874         isNavKeyPress : function(){
6875             var k = this.keyCode;
6876             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6877             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6878         },
6879
6880         isSpecialKey : function(){
6881             var k = this.keyCode;
6882             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6883             (k == 16) || (k == 17) ||
6884             (k >= 18 && k <= 20) ||
6885             (k >= 33 && k <= 35) ||
6886             (k >= 36 && k <= 39) ||
6887             (k >= 44 && k <= 45);
6888         },
6889         /**
6890          * Cancels bubbling of the event.
6891          */
6892         stopPropagation : function(){
6893             if(this.browserEvent){
6894                 if(this.type == 'mousedown'){
6895                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6896                 }
6897                 E.stopPropagation(this.browserEvent);
6898             }
6899         },
6900
6901         /**
6902          * Gets the key code for the event.
6903          * @return {Number}
6904          */
6905         getCharCode : function(){
6906             return this.charCode || this.keyCode;
6907         },
6908
6909         /**
6910          * Returns a normalized keyCode for the event.
6911          * @return {Number} The key code
6912          */
6913         getKey : function(){
6914             var k = this.keyCode || this.charCode;
6915             return Roo.isSafari ? (safariKeys[k] || k) : k;
6916         },
6917
6918         /**
6919          * Gets the x coordinate of the event.
6920          * @return {Number}
6921          */
6922         getPageX : function(){
6923             return this.xy[0];
6924         },
6925
6926         /**
6927          * Gets the y coordinate of the event.
6928          * @return {Number}
6929          */
6930         getPageY : function(){
6931             return this.xy[1];
6932         },
6933
6934         /**
6935          * Gets the time of the event.
6936          * @return {Number}
6937          */
6938         getTime : function(){
6939             if(this.browserEvent){
6940                 return E.getTime(this.browserEvent);
6941             }
6942             return null;
6943         },
6944
6945         /**
6946          * Gets the page coordinates of the event.
6947          * @return {Array} The xy values like [x, y]
6948          */
6949         getXY : function(){
6950             return this.xy;
6951         },
6952
6953         /**
6954          * Gets the target for the event.
6955          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6956          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6957                 search as a number or element (defaults to 10 || document.body)
6958          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6959          * @return {HTMLelement}
6960          */
6961         getTarget : function(selector, maxDepth, returnEl){
6962             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6963         },
6964         /**
6965          * Gets the related target.
6966          * @return {HTMLElement}
6967          */
6968         getRelatedTarget : function(){
6969             if(this.browserEvent){
6970                 return E.getRelatedTarget(this.browserEvent);
6971             }
6972             return null;
6973         },
6974
6975         /**
6976          * Normalizes mouse wheel delta across browsers
6977          * @return {Number} The delta
6978          */
6979         getWheelDelta : function(){
6980             var e = this.browserEvent;
6981             var delta = 0;
6982             if(e.wheelDelta){ /* IE/Opera. */
6983                 delta = e.wheelDelta/120;
6984             }else if(e.detail){ /* Mozilla case. */
6985                 delta = -e.detail/3;
6986             }
6987             return delta;
6988         },
6989
6990         /**
6991          * Returns true if the control, meta, shift or alt key was pressed during this event.
6992          * @return {Boolean}
6993          */
6994         hasModifier : function(){
6995             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6996         },
6997
6998         /**
6999          * Returns true if the target of this event equals el or is a child of el
7000          * @param {String/HTMLElement/Element} el
7001          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7002          * @return {Boolean}
7003          */
7004         within : function(el, related){
7005             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7006             return t && Roo.fly(el).contains(t);
7007         },
7008
7009         getPoint : function(){
7010             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7011         }
7012     };
7013
7014     return new Roo.EventObjectImpl();
7015 }();
7016             
7017     /*
7018  * Based on:
7019  * Ext JS Library 1.1.1
7020  * Copyright(c) 2006-2007, Ext JS, LLC.
7021  *
7022  * Originally Released Under LGPL - original licence link has changed is not relivant.
7023  *
7024  * Fork - LGPL
7025  * <script type="text/javascript">
7026  */
7027
7028  
7029 // was in Composite Element!??!?!
7030  
7031 (function(){
7032     var D = Roo.lib.Dom;
7033     var E = Roo.lib.Event;
7034     var A = Roo.lib.Anim;
7035
7036     // local style camelizing for speed
7037     var propCache = {};
7038     var camelRe = /(-[a-z])/gi;
7039     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7040     var view = document.defaultView;
7041
7042 /**
7043  * @class Roo.Element
7044  * Represents an Element in the DOM.<br><br>
7045  * Usage:<br>
7046 <pre><code>
7047 var el = Roo.get("my-div");
7048
7049 // or with getEl
7050 var el = getEl("my-div");
7051
7052 // or with a DOM element
7053 var el = Roo.get(myDivElement);
7054 </code></pre>
7055  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7056  * each call instead of constructing a new one.<br><br>
7057  * <b>Animations</b><br />
7058  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7059  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7060 <pre>
7061 Option    Default   Description
7062 --------- --------  ---------------------------------------------
7063 duration  .35       The duration of the animation in seconds
7064 easing    easeOut   The YUI easing method
7065 callback  none      A function to execute when the anim completes
7066 scope     this      The scope (this) of the callback function
7067 </pre>
7068 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7069 * manipulate the animation. Here's an example:
7070 <pre><code>
7071 var el = Roo.get("my-div");
7072
7073 // no animation
7074 el.setWidth(100);
7075
7076 // default animation
7077 el.setWidth(100, true);
7078
7079 // animation with some options set
7080 el.setWidth(100, {
7081     duration: 1,
7082     callback: this.foo,
7083     scope: this
7084 });
7085
7086 // using the "anim" property to get the Anim object
7087 var opt = {
7088     duration: 1,
7089     callback: this.foo,
7090     scope: this
7091 };
7092 el.setWidth(100, opt);
7093 ...
7094 if(opt.anim.isAnimated()){
7095     opt.anim.stop();
7096 }
7097 </code></pre>
7098 * <b> Composite (Collections of) Elements</b><br />
7099  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7100  * @constructor Create a new Element directly.
7101  * @param {String/HTMLElement} element
7102  * @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).
7103  */
7104     Roo.Element = function(element, forceNew){
7105         var dom = typeof element == "string" ?
7106                 document.getElementById(element) : element;
7107         if(!dom){ // invalid id/element
7108             return null;
7109         }
7110         var id = dom.id;
7111         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7112             return Roo.Element.cache[id];
7113         }
7114
7115         /**
7116          * The DOM element
7117          * @type HTMLElement
7118          */
7119         this.dom = dom;
7120
7121         /**
7122          * The DOM element ID
7123          * @type String
7124          */
7125         this.id = id || Roo.id(dom);
7126     };
7127
7128     var El = Roo.Element;
7129
7130     El.prototype = {
7131         /**
7132          * The element's default display mode  (defaults to "")
7133          * @type String
7134          */
7135         originalDisplay : "",
7136
7137         visibilityMode : 1,
7138         /**
7139          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7140          * @type String
7141          */
7142         defaultUnit : "px",
7143         
7144         /**
7145          * Sets the element's visibility mode. When setVisible() is called it
7146          * will use this to determine whether to set the visibility or the display property.
7147          * @param visMode Element.VISIBILITY or Element.DISPLAY
7148          * @return {Roo.Element} this
7149          */
7150         setVisibilityMode : function(visMode){
7151             this.visibilityMode = visMode;
7152             return this;
7153         },
7154         /**
7155          * Convenience method for setVisibilityMode(Element.DISPLAY)
7156          * @param {String} display (optional) What to set display to when visible
7157          * @return {Roo.Element} this
7158          */
7159         enableDisplayMode : function(display){
7160             this.setVisibilityMode(El.DISPLAY);
7161             if(typeof display != "undefined") { this.originalDisplay = display; }
7162             return this;
7163         },
7164
7165         /**
7166          * 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)
7167          * @param {String} selector The simple selector to test
7168          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7169                 search as a number or element (defaults to 10 || document.body)
7170          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7171          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7172          */
7173         findParent : function(simpleSelector, maxDepth, returnEl){
7174             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7175             maxDepth = maxDepth || 50;
7176             if(typeof maxDepth != "number"){
7177                 stopEl = Roo.getDom(maxDepth);
7178                 maxDepth = 10;
7179             }
7180             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7181                 if(dq.is(p, simpleSelector)){
7182                     return returnEl ? Roo.get(p) : p;
7183                 }
7184                 depth++;
7185                 p = p.parentNode;
7186             }
7187             return null;
7188         },
7189
7190
7191         /**
7192          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7193          * @param {String} selector The simple selector to test
7194          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7195                 search as a number or element (defaults to 10 || document.body)
7196          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7197          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7198          */
7199         findParentNode : function(simpleSelector, maxDepth, returnEl){
7200             var p = Roo.fly(this.dom.parentNode, '_internal');
7201             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7202         },
7203         
7204         /**
7205          * Looks at  the scrollable parent element
7206          */
7207         findScrollableParent : function()
7208         {
7209             var overflowRegex = /(auto|scroll)/;
7210             
7211             if(this.getStyle('position') === 'fixed'){
7212                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7213             }
7214             
7215             var excludeStaticParent = this.getStyle('position') === "absolute";
7216             
7217             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7218                 
7219                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7220                     continue;
7221                 }
7222                 
7223                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7224                     return parent;
7225                 }
7226                 
7227                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7228                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7229                 }
7230             }
7231             
7232             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7233         },
7234
7235         /**
7236          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7237          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7238          * @param {String} selector The simple selector to test
7239          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7240                 search as a number or element (defaults to 10 || document.body)
7241          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7242          */
7243         up : function(simpleSelector, maxDepth){
7244             return this.findParentNode(simpleSelector, maxDepth, true);
7245         },
7246
7247
7248
7249         /**
7250          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7251          * @param {String} selector The simple selector to test
7252          * @return {Boolean} True if this element matches the selector, else false
7253          */
7254         is : function(simpleSelector){
7255             return Roo.DomQuery.is(this.dom, simpleSelector);
7256         },
7257
7258         /**
7259          * Perform animation on this element.
7260          * @param {Object} args The YUI animation control args
7261          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7262          * @param {Function} onComplete (optional) Function to call when animation completes
7263          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7264          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7265          * @return {Roo.Element} this
7266          */
7267         animate : function(args, duration, onComplete, easing, animType){
7268             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7269             return this;
7270         },
7271
7272         /*
7273          * @private Internal animation call
7274          */
7275         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7276             animType = animType || 'run';
7277             opt = opt || {};
7278             var anim = Roo.lib.Anim[animType](
7279                 this.dom, args,
7280                 (opt.duration || defaultDur) || .35,
7281                 (opt.easing || defaultEase) || 'easeOut',
7282                 function(){
7283                     Roo.callback(cb, this);
7284                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7285                 },
7286                 this
7287             );
7288             opt.anim = anim;
7289             return anim;
7290         },
7291
7292         // private legacy anim prep
7293         preanim : function(a, i){
7294             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7295         },
7296
7297         /**
7298          * Removes worthless text nodes
7299          * @param {Boolean} forceReclean (optional) By default the element
7300          * keeps track if it has been cleaned already so
7301          * you can call this over and over. However, if you update the element and
7302          * need to force a reclean, you can pass true.
7303          */
7304         clean : function(forceReclean){
7305             if(this.isCleaned && forceReclean !== true){
7306                 return this;
7307             }
7308             var ns = /\S/;
7309             var d = this.dom, n = d.firstChild, ni = -1;
7310             while(n){
7311                 var nx = n.nextSibling;
7312                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7313                     d.removeChild(n);
7314                 }else{
7315                     n.nodeIndex = ++ni;
7316                 }
7317                 n = nx;
7318             }
7319             this.isCleaned = true;
7320             return this;
7321         },
7322
7323         // private
7324         calcOffsetsTo : function(el){
7325             el = Roo.get(el);
7326             var d = el.dom;
7327             var restorePos = false;
7328             if(el.getStyle('position') == 'static'){
7329                 el.position('relative');
7330                 restorePos = true;
7331             }
7332             var x = 0, y =0;
7333             var op = this.dom;
7334             while(op && op != d && op.tagName != 'HTML'){
7335                 x+= op.offsetLeft;
7336                 y+= op.offsetTop;
7337                 op = op.offsetParent;
7338             }
7339             if(restorePos){
7340                 el.position('static');
7341             }
7342             return [x, y];
7343         },
7344
7345         /**
7346          * Scrolls this element into view within the passed container.
7347          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7348          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7349          * @return {Roo.Element} this
7350          */
7351         scrollIntoView : function(container, hscroll){
7352             var c = Roo.getDom(container) || document.body;
7353             var el = this.dom;
7354
7355             var o = this.calcOffsetsTo(c),
7356                 l = o[0],
7357                 t = o[1],
7358                 b = t+el.offsetHeight,
7359                 r = l+el.offsetWidth;
7360
7361             var ch = c.clientHeight;
7362             var ct = parseInt(c.scrollTop, 10);
7363             var cl = parseInt(c.scrollLeft, 10);
7364             var cb = ct + ch;
7365             var cr = cl + c.clientWidth;
7366
7367             if(t < ct){
7368                 c.scrollTop = t;
7369             }else if(b > cb){
7370                 c.scrollTop = b-ch;
7371             }
7372
7373             if(hscroll !== false){
7374                 if(l < cl){
7375                     c.scrollLeft = l;
7376                 }else if(r > cr){
7377                     c.scrollLeft = r-c.clientWidth;
7378                 }
7379             }
7380             return this;
7381         },
7382
7383         // private
7384         scrollChildIntoView : function(child, hscroll){
7385             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7386         },
7387
7388         /**
7389          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7390          * the new height may not be available immediately.
7391          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7392          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7393          * @param {Function} onComplete (optional) Function to call when animation completes
7394          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7395          * @return {Roo.Element} this
7396          */
7397         autoHeight : function(animate, duration, onComplete, easing){
7398             var oldHeight = this.getHeight();
7399             this.clip();
7400             this.setHeight(1); // force clipping
7401             setTimeout(function(){
7402                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7403                 if(!animate){
7404                     this.setHeight(height);
7405                     this.unclip();
7406                     if(typeof onComplete == "function"){
7407                         onComplete();
7408                     }
7409                 }else{
7410                     this.setHeight(oldHeight); // restore original height
7411                     this.setHeight(height, animate, duration, function(){
7412                         this.unclip();
7413                         if(typeof onComplete == "function") { onComplete(); }
7414                     }.createDelegate(this), easing);
7415                 }
7416             }.createDelegate(this), 0);
7417             return this;
7418         },
7419
7420         /**
7421          * Returns true if this element is an ancestor of the passed element
7422          * @param {HTMLElement/String} el The element to check
7423          * @return {Boolean} True if this element is an ancestor of el, else false
7424          */
7425         contains : function(el){
7426             if(!el){return false;}
7427             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7428         },
7429
7430         /**
7431          * Checks whether the element is currently visible using both visibility and display properties.
7432          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7433          * @return {Boolean} True if the element is currently visible, else false
7434          */
7435         isVisible : function(deep) {
7436             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7437             if(deep !== true || !vis){
7438                 return vis;
7439             }
7440             var p = this.dom.parentNode;
7441             while(p && p.tagName.toLowerCase() != "body"){
7442                 if(!Roo.fly(p, '_isVisible').isVisible()){
7443                     return false;
7444                 }
7445                 p = p.parentNode;
7446             }
7447             return true;
7448         },
7449
7450         /**
7451          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7452          * @param {String} selector The CSS selector
7453          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7454          * @return {CompositeElement/CompositeElementLite} The composite element
7455          */
7456         select : function(selector, unique){
7457             return El.select(selector, unique, this.dom);
7458         },
7459
7460         /**
7461          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7462          * @param {String} selector The CSS selector
7463          * @return {Array} An array of the matched nodes
7464          */
7465         query : function(selector, unique){
7466             return Roo.DomQuery.select(selector, this.dom);
7467         },
7468
7469         /**
7470          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7471          * @param {String} selector The CSS selector
7472          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7473          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7474          */
7475         child : function(selector, returnDom){
7476             var n = Roo.DomQuery.selectNode(selector, this.dom);
7477             return returnDom ? n : Roo.get(n);
7478         },
7479
7480         /**
7481          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7482          * @param {String} selector The CSS selector
7483          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7484          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7485          */
7486         down : function(selector, returnDom){
7487             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7488             return returnDom ? n : Roo.get(n);
7489         },
7490
7491         /**
7492          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7493          * @param {String} group The group the DD object is member of
7494          * @param {Object} config The DD config object
7495          * @param {Object} overrides An object containing methods to override/implement on the DD object
7496          * @return {Roo.dd.DD} The DD object
7497          */
7498         initDD : function(group, config, overrides){
7499             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7500             return Roo.apply(dd, overrides);
7501         },
7502
7503         /**
7504          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7505          * @param {String} group The group the DDProxy object is member of
7506          * @param {Object} config The DDProxy config object
7507          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7508          * @return {Roo.dd.DDProxy} The DDProxy object
7509          */
7510         initDDProxy : function(group, config, overrides){
7511             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7512             return Roo.apply(dd, overrides);
7513         },
7514
7515         /**
7516          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7517          * @param {String} group The group the DDTarget object is member of
7518          * @param {Object} config The DDTarget config object
7519          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7520          * @return {Roo.dd.DDTarget} The DDTarget object
7521          */
7522         initDDTarget : function(group, config, overrides){
7523             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7524             return Roo.apply(dd, overrides);
7525         },
7526
7527         /**
7528          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7529          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7530          * @param {Boolean} visible Whether the element is visible
7531          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7532          * @return {Roo.Element} this
7533          */
7534          setVisible : function(visible, animate){
7535             if(!animate || !A){
7536                 if(this.visibilityMode == El.DISPLAY){
7537                     this.setDisplayed(visible);
7538                 }else{
7539                     this.fixDisplay();
7540                     this.dom.style.visibility = visible ? "visible" : "hidden";
7541                 }
7542             }else{
7543                 // closure for composites
7544                 var dom = this.dom;
7545                 var visMode = this.visibilityMode;
7546                 if(visible){
7547                     this.setOpacity(.01);
7548                     this.setVisible(true);
7549                 }
7550                 this.anim({opacity: { to: (visible?1:0) }},
7551                       this.preanim(arguments, 1),
7552                       null, .35, 'easeIn', function(){
7553                          if(!visible){
7554                              if(visMode == El.DISPLAY){
7555                                  dom.style.display = "none";
7556                              }else{
7557                                  dom.style.visibility = "hidden";
7558                              }
7559                              Roo.get(dom).setOpacity(1);
7560                          }
7561                      });
7562             }
7563             return this;
7564         },
7565
7566         /**
7567          * Returns true if display is not "none"
7568          * @return {Boolean}
7569          */
7570         isDisplayed : function() {
7571             return this.getStyle("display") != "none";
7572         },
7573
7574         /**
7575          * Toggles the element's visibility or display, depending on visibility mode.
7576          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7577          * @return {Roo.Element} this
7578          */
7579         toggle : function(animate){
7580             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7581             return this;
7582         },
7583
7584         /**
7585          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7586          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7587          * @return {Roo.Element} this
7588          */
7589         setDisplayed : function(value) {
7590             if(typeof value == "boolean"){
7591                value = value ? this.originalDisplay : "none";
7592             }
7593             this.setStyle("display", value);
7594             return this;
7595         },
7596
7597         /**
7598          * Tries to focus the element. Any exceptions are caught and ignored.
7599          * @return {Roo.Element} this
7600          */
7601         focus : function() {
7602             try{
7603                 this.dom.focus();
7604             }catch(e){}
7605             return this;
7606         },
7607
7608         /**
7609          * Tries to blur the element. Any exceptions are caught and ignored.
7610          * @return {Roo.Element} this
7611          */
7612         blur : function() {
7613             try{
7614                 this.dom.blur();
7615             }catch(e){}
7616             return this;
7617         },
7618
7619         /**
7620          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7621          * @param {String/Array} className The CSS class to add, or an array of classes
7622          * @return {Roo.Element} this
7623          */
7624         addClass : function(className){
7625             if(className instanceof Array){
7626                 for(var i = 0, len = className.length; i < len; i++) {
7627                     this.addClass(className[i]);
7628                 }
7629             }else{
7630                 if(className && !this.hasClass(className)){
7631                     this.dom.className = this.dom.className + " " + className;
7632                 }
7633             }
7634             return this;
7635         },
7636
7637         /**
7638          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7639          * @param {String/Array} className The CSS class to add, or an array of classes
7640          * @return {Roo.Element} this
7641          */
7642         radioClass : function(className){
7643             var siblings = this.dom.parentNode.childNodes;
7644             for(var i = 0; i < siblings.length; i++) {
7645                 var s = siblings[i];
7646                 if(s.nodeType == 1){
7647                     Roo.get(s).removeClass(className);
7648                 }
7649             }
7650             this.addClass(className);
7651             return this;
7652         },
7653
7654         /**
7655          * Removes one or more CSS classes from the element.
7656          * @param {String/Array} className The CSS class to remove, or an array of classes
7657          * @return {Roo.Element} this
7658          */
7659         removeClass : function(className){
7660             if(!className || !this.dom.className){
7661                 return this;
7662             }
7663             if(className instanceof Array){
7664                 for(var i = 0, len = className.length; i < len; i++) {
7665                     this.removeClass(className[i]);
7666                 }
7667             }else{
7668                 if(this.hasClass(className)){
7669                     var re = this.classReCache[className];
7670                     if (!re) {
7671                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7672                        this.classReCache[className] = re;
7673                     }
7674                     this.dom.className =
7675                         this.dom.className.replace(re, " ");
7676                 }
7677             }
7678             return this;
7679         },
7680
7681         // private
7682         classReCache: {},
7683
7684         /**
7685          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7686          * @param {String} className The CSS class to toggle
7687          * @return {Roo.Element} this
7688          */
7689         toggleClass : function(className){
7690             if(this.hasClass(className)){
7691                 this.removeClass(className);
7692             }else{
7693                 this.addClass(className);
7694             }
7695             return this;
7696         },
7697
7698         /**
7699          * Checks if the specified CSS class exists on this element's DOM node.
7700          * @param {String} className The CSS class to check for
7701          * @return {Boolean} True if the class exists, else false
7702          */
7703         hasClass : function(className){
7704             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7705         },
7706
7707         /**
7708          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7709          * @param {String} oldClassName The CSS class to replace
7710          * @param {String} newClassName The replacement CSS class
7711          * @return {Roo.Element} this
7712          */
7713         replaceClass : function(oldClassName, newClassName){
7714             this.removeClass(oldClassName);
7715             this.addClass(newClassName);
7716             return this;
7717         },
7718
7719         /**
7720          * Returns an object with properties matching the styles requested.
7721          * For example, el.getStyles('color', 'font-size', 'width') might return
7722          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7723          * @param {String} style1 A style name
7724          * @param {String} style2 A style name
7725          * @param {String} etc.
7726          * @return {Object} The style object
7727          */
7728         getStyles : function(){
7729             var a = arguments, len = a.length, r = {};
7730             for(var i = 0; i < len; i++){
7731                 r[a[i]] = this.getStyle(a[i]);
7732             }
7733             return r;
7734         },
7735
7736         /**
7737          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7738          * @param {String} property The style property whose value is returned.
7739          * @return {String} The current value of the style property for this element.
7740          */
7741         getStyle : function(){
7742             return view && view.getComputedStyle ?
7743                 function(prop){
7744                     var el = this.dom, v, cs, camel;
7745                     if(prop == 'float'){
7746                         prop = "cssFloat";
7747                     }
7748                     if(el.style && (v = el.style[prop])){
7749                         return v;
7750                     }
7751                     if(cs = view.getComputedStyle(el, "")){
7752                         if(!(camel = propCache[prop])){
7753                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7754                         }
7755                         return cs[camel];
7756                     }
7757                     return null;
7758                 } :
7759                 function(prop){
7760                     var el = this.dom, v, cs, camel;
7761                     if(prop == 'opacity'){
7762                         if(typeof el.style.filter == 'string'){
7763                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7764                             if(m){
7765                                 var fv = parseFloat(m[1]);
7766                                 if(!isNaN(fv)){
7767                                     return fv ? fv / 100 : 0;
7768                                 }
7769                             }
7770                         }
7771                         return 1;
7772                     }else if(prop == 'float'){
7773                         prop = "styleFloat";
7774                     }
7775                     if(!(camel = propCache[prop])){
7776                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7777                     }
7778                     if(v = el.style[camel]){
7779                         return v;
7780                     }
7781                     if(cs = el.currentStyle){
7782                         return cs[camel];
7783                     }
7784                     return null;
7785                 };
7786         }(),
7787
7788         /**
7789          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7790          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7791          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7792          * @return {Roo.Element} this
7793          */
7794         setStyle : function(prop, value){
7795             if(typeof prop == "string"){
7796                 
7797                 if (prop == 'float') {
7798                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7799                     return this;
7800                 }
7801                 
7802                 var camel;
7803                 if(!(camel = propCache[prop])){
7804                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7805                 }
7806                 
7807                 if(camel == 'opacity') {
7808                     this.setOpacity(value);
7809                 }else{
7810                     this.dom.style[camel] = value;
7811                 }
7812             }else{
7813                 for(var style in prop){
7814                     if(typeof prop[style] != "function"){
7815                        this.setStyle(style, prop[style]);
7816                     }
7817                 }
7818             }
7819             return this;
7820         },
7821
7822         /**
7823          * More flexible version of {@link #setStyle} for setting style properties.
7824          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7825          * a function which returns such a specification.
7826          * @return {Roo.Element} this
7827          */
7828         applyStyles : function(style){
7829             Roo.DomHelper.applyStyles(this.dom, style);
7830             return this;
7831         },
7832
7833         /**
7834           * 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).
7835           * @return {Number} The X position of the element
7836           */
7837         getX : function(){
7838             return D.getX(this.dom);
7839         },
7840
7841         /**
7842           * 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).
7843           * @return {Number} The Y position of the element
7844           */
7845         getY : function(){
7846             return D.getY(this.dom);
7847         },
7848
7849         /**
7850           * 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).
7851           * @return {Array} The XY position of the element
7852           */
7853         getXY : function(){
7854             return D.getXY(this.dom);
7855         },
7856
7857         /**
7858          * 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).
7859          * @param {Number} The X position of the element
7860          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7861          * @return {Roo.Element} this
7862          */
7863         setX : function(x, animate){
7864             if(!animate || !A){
7865                 D.setX(this.dom, x);
7866             }else{
7867                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7868             }
7869             return this;
7870         },
7871
7872         /**
7873          * 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).
7874          * @param {Number} The Y position of the element
7875          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7876          * @return {Roo.Element} this
7877          */
7878         setY : function(y, animate){
7879             if(!animate || !A){
7880                 D.setY(this.dom, y);
7881             }else{
7882                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7883             }
7884             return this;
7885         },
7886
7887         /**
7888          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7889          * @param {String} left The left CSS property value
7890          * @return {Roo.Element} this
7891          */
7892         setLeft : function(left){
7893             this.setStyle("left", this.addUnits(left));
7894             return this;
7895         },
7896
7897         /**
7898          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7899          * @param {String} top The top CSS property value
7900          * @return {Roo.Element} this
7901          */
7902         setTop : function(top){
7903             this.setStyle("top", this.addUnits(top));
7904             return this;
7905         },
7906
7907         /**
7908          * Sets the element's CSS right style.
7909          * @param {String} right The right CSS property value
7910          * @return {Roo.Element} this
7911          */
7912         setRight : function(right){
7913             this.setStyle("right", this.addUnits(right));
7914             return this;
7915         },
7916
7917         /**
7918          * Sets the element's CSS bottom style.
7919          * @param {String} bottom The bottom CSS property value
7920          * @return {Roo.Element} this
7921          */
7922         setBottom : function(bottom){
7923             this.setStyle("bottom", this.addUnits(bottom));
7924             return this;
7925         },
7926
7927         /**
7928          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7929          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7930          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7931          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7932          * @return {Roo.Element} this
7933          */
7934         setXY : function(pos, animate){
7935             if(!animate || !A){
7936                 D.setXY(this.dom, pos);
7937             }else{
7938                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7939             }
7940             return this;
7941         },
7942
7943         /**
7944          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7945          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7946          * @param {Number} x X value for new position (coordinates are page-based)
7947          * @param {Number} y Y value for new position (coordinates are page-based)
7948          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7949          * @return {Roo.Element} this
7950          */
7951         setLocation : function(x, y, animate){
7952             this.setXY([x, y], this.preanim(arguments, 2));
7953             return this;
7954         },
7955
7956         /**
7957          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7958          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7959          * @param {Number} x X value for new position (coordinates are page-based)
7960          * @param {Number} y Y value for new position (coordinates are page-based)
7961          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7962          * @return {Roo.Element} this
7963          */
7964         moveTo : function(x, y, animate){
7965             this.setXY([x, y], this.preanim(arguments, 2));
7966             return this;
7967         },
7968
7969         /**
7970          * Returns the region of the given element.
7971          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7972          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7973          */
7974         getRegion : function(){
7975             return D.getRegion(this.dom);
7976         },
7977
7978         /**
7979          * Returns the offset height of the element
7980          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7981          * @return {Number} The element's height
7982          */
7983         getHeight : function(contentHeight){
7984             var h = this.dom.offsetHeight || 0;
7985             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7986         },
7987
7988         /**
7989          * Returns the offset width of the element
7990          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7991          * @return {Number} The element's width
7992          */
7993         getWidth : function(contentWidth){
7994             var w = this.dom.offsetWidth || 0;
7995             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7996         },
7997
7998         /**
7999          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8000          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8001          * if a height has not been set using CSS.
8002          * @return {Number}
8003          */
8004         getComputedHeight : function(){
8005             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8006             if(!h){
8007                 h = parseInt(this.getStyle('height'), 10) || 0;
8008                 if(!this.isBorderBox()){
8009                     h += this.getFrameWidth('tb');
8010                 }
8011             }
8012             return h;
8013         },
8014
8015         /**
8016          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8017          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8018          * if a width has not been set using CSS.
8019          * @return {Number}
8020          */
8021         getComputedWidth : function(){
8022             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8023             if(!w){
8024                 w = parseInt(this.getStyle('width'), 10) || 0;
8025                 if(!this.isBorderBox()){
8026                     w += this.getFrameWidth('lr');
8027                 }
8028             }
8029             return w;
8030         },
8031
8032         /**
8033          * Returns the size of the element.
8034          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8035          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8036          */
8037         getSize : function(contentSize){
8038             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8039         },
8040
8041         /**
8042          * Returns the width and height of the viewport.
8043          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8044          */
8045         getViewSize : function(){
8046             var d = this.dom, doc = document, aw = 0, ah = 0;
8047             if(d == doc || d == doc.body){
8048                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8049             }else{
8050                 return {
8051                     width : d.clientWidth,
8052                     height: d.clientHeight
8053                 };
8054             }
8055         },
8056
8057         /**
8058          * Returns the value of the "value" attribute
8059          * @param {Boolean} asNumber true to parse the value as a number
8060          * @return {String/Number}
8061          */
8062         getValue : function(asNumber){
8063             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8064         },
8065
8066         // private
8067         adjustWidth : function(width){
8068             if(typeof width == "number"){
8069                 if(this.autoBoxAdjust && !this.isBorderBox()){
8070                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8071                 }
8072                 if(width < 0){
8073                     width = 0;
8074                 }
8075             }
8076             return width;
8077         },
8078
8079         // private
8080         adjustHeight : function(height){
8081             if(typeof height == "number"){
8082                if(this.autoBoxAdjust && !this.isBorderBox()){
8083                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8084                }
8085                if(height < 0){
8086                    height = 0;
8087                }
8088             }
8089             return height;
8090         },
8091
8092         /**
8093          * Set the width of the element
8094          * @param {Number} width The new width
8095          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8096          * @return {Roo.Element} this
8097          */
8098         setWidth : function(width, animate){
8099             width = this.adjustWidth(width);
8100             if(!animate || !A){
8101                 this.dom.style.width = this.addUnits(width);
8102             }else{
8103                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8104             }
8105             return this;
8106         },
8107
8108         /**
8109          * Set the height of the element
8110          * @param {Number} height The new height
8111          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8112          * @return {Roo.Element} this
8113          */
8114          setHeight : function(height, animate){
8115             height = this.adjustHeight(height);
8116             if(!animate || !A){
8117                 this.dom.style.height = this.addUnits(height);
8118             }else{
8119                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8120             }
8121             return this;
8122         },
8123
8124         /**
8125          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8126          * @param {Number} width The new width
8127          * @param {Number} height The new height
8128          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8129          * @return {Roo.Element} this
8130          */
8131          setSize : function(width, height, animate){
8132             if(typeof width == "object"){ // in case of object from getSize()
8133                 height = width.height; width = width.width;
8134             }
8135             width = this.adjustWidth(width); height = this.adjustHeight(height);
8136             if(!animate || !A){
8137                 this.dom.style.width = this.addUnits(width);
8138                 this.dom.style.height = this.addUnits(height);
8139             }else{
8140                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8141             }
8142             return this;
8143         },
8144
8145         /**
8146          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8147          * @param {Number} x X value for new position (coordinates are page-based)
8148          * @param {Number} y Y value for new position (coordinates are page-based)
8149          * @param {Number} width The new width
8150          * @param {Number} height The new height
8151          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8152          * @return {Roo.Element} this
8153          */
8154         setBounds : function(x, y, width, height, animate){
8155             if(!animate || !A){
8156                 this.setSize(width, height);
8157                 this.setLocation(x, y);
8158             }else{
8159                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8160                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8161                               this.preanim(arguments, 4), 'motion');
8162             }
8163             return this;
8164         },
8165
8166         /**
8167          * 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.
8168          * @param {Roo.lib.Region} region The region to fill
8169          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8170          * @return {Roo.Element} this
8171          */
8172         setRegion : function(region, animate){
8173             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8174             return this;
8175         },
8176
8177         /**
8178          * Appends an event handler
8179          *
8180          * @param {String}   eventName     The type of event to append
8181          * @param {Function} fn        The method the event invokes
8182          * @param {Object} scope       (optional) The scope (this object) of the fn
8183          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8184          */
8185         addListener : function(eventName, fn, scope, options){
8186             if (this.dom) {
8187                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8188             }
8189         },
8190
8191         /**
8192          * Removes an event handler from this element
8193          * @param {String} eventName the type of event to remove
8194          * @param {Function} fn the method the event invokes
8195          * @return {Roo.Element} this
8196          */
8197         removeListener : function(eventName, fn){
8198             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8199             return this;
8200         },
8201
8202         /**
8203          * Removes all previous added listeners from this element
8204          * @return {Roo.Element} this
8205          */
8206         removeAllListeners : function(){
8207             E.purgeElement(this.dom);
8208             return this;
8209         },
8210
8211         relayEvent : function(eventName, observable){
8212             this.on(eventName, function(e){
8213                 observable.fireEvent(eventName, e);
8214             });
8215         },
8216
8217         /**
8218          * Set the opacity of the element
8219          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8220          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8221          * @return {Roo.Element} this
8222          */
8223          setOpacity : function(opacity, animate){
8224             if(!animate || !A){
8225                 var s = this.dom.style;
8226                 if(Roo.isIE){
8227                     s.zoom = 1;
8228                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8229                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8230                 }else{
8231                     s.opacity = opacity;
8232                 }
8233             }else{
8234                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8235             }
8236             return this;
8237         },
8238
8239         /**
8240          * Gets the left X coordinate
8241          * @param {Boolean} local True to get the local css position instead of page coordinate
8242          * @return {Number}
8243          */
8244         getLeft : function(local){
8245             if(!local){
8246                 return this.getX();
8247             }else{
8248                 return parseInt(this.getStyle("left"), 10) || 0;
8249             }
8250         },
8251
8252         /**
8253          * Gets the right X coordinate of the element (element X position + element width)
8254          * @param {Boolean} local True to get the local css position instead of page coordinate
8255          * @return {Number}
8256          */
8257         getRight : function(local){
8258             if(!local){
8259                 return this.getX() + this.getWidth();
8260             }else{
8261                 return (this.getLeft(true) + this.getWidth()) || 0;
8262             }
8263         },
8264
8265         /**
8266          * Gets the top Y coordinate
8267          * @param {Boolean} local True to get the local css position instead of page coordinate
8268          * @return {Number}
8269          */
8270         getTop : function(local) {
8271             if(!local){
8272                 return this.getY();
8273             }else{
8274                 return parseInt(this.getStyle("top"), 10) || 0;
8275             }
8276         },
8277
8278         /**
8279          * Gets the bottom Y coordinate of the element (element Y position + element height)
8280          * @param {Boolean} local True to get the local css position instead of page coordinate
8281          * @return {Number}
8282          */
8283         getBottom : function(local){
8284             if(!local){
8285                 return this.getY() + this.getHeight();
8286             }else{
8287                 return (this.getTop(true) + this.getHeight()) || 0;
8288             }
8289         },
8290
8291         /**
8292         * Initializes positioning on this element. If a desired position is not passed, it will make the
8293         * the element positioned relative IF it is not already positioned.
8294         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8295         * @param {Number} zIndex (optional) The zIndex to apply
8296         * @param {Number} x (optional) Set the page X position
8297         * @param {Number} y (optional) Set the page Y position
8298         */
8299         position : function(pos, zIndex, x, y){
8300             if(!pos){
8301                if(this.getStyle('position') == 'static'){
8302                    this.setStyle('position', 'relative');
8303                }
8304             }else{
8305                 this.setStyle("position", pos);
8306             }
8307             if(zIndex){
8308                 this.setStyle("z-index", zIndex);
8309             }
8310             if(x !== undefined && y !== undefined){
8311                 this.setXY([x, y]);
8312             }else if(x !== undefined){
8313                 this.setX(x);
8314             }else if(y !== undefined){
8315                 this.setY(y);
8316             }
8317         },
8318
8319         /**
8320         * Clear positioning back to the default when the document was loaded
8321         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8322         * @return {Roo.Element} this
8323          */
8324         clearPositioning : function(value){
8325             value = value ||'';
8326             this.setStyle({
8327                 "left": value,
8328                 "right": value,
8329                 "top": value,
8330                 "bottom": value,
8331                 "z-index": "",
8332                 "position" : "static"
8333             });
8334             return this;
8335         },
8336
8337         /**
8338         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8339         * snapshot before performing an update and then restoring the element.
8340         * @return {Object}
8341         */
8342         getPositioning : function(){
8343             var l = this.getStyle("left");
8344             var t = this.getStyle("top");
8345             return {
8346                 "position" : this.getStyle("position"),
8347                 "left" : l,
8348                 "right" : l ? "" : this.getStyle("right"),
8349                 "top" : t,
8350                 "bottom" : t ? "" : this.getStyle("bottom"),
8351                 "z-index" : this.getStyle("z-index")
8352             };
8353         },
8354
8355         /**
8356          * Gets the width of the border(s) for the specified side(s)
8357          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8358          * passing lr would get the border (l)eft width + the border (r)ight width.
8359          * @return {Number} The width of the sides passed added together
8360          */
8361         getBorderWidth : function(side){
8362             return this.addStyles(side, El.borders);
8363         },
8364
8365         /**
8366          * Gets the width of the padding(s) for the specified side(s)
8367          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8368          * passing lr would get the padding (l)eft + the padding (r)ight.
8369          * @return {Number} The padding of the sides passed added together
8370          */
8371         getPadding : function(side){
8372             return this.addStyles(side, El.paddings);
8373         },
8374
8375         /**
8376         * Set positioning with an object returned by getPositioning().
8377         * @param {Object} posCfg
8378         * @return {Roo.Element} this
8379          */
8380         setPositioning : function(pc){
8381             this.applyStyles(pc);
8382             if(pc.right == "auto"){
8383                 this.dom.style.right = "";
8384             }
8385             if(pc.bottom == "auto"){
8386                 this.dom.style.bottom = "";
8387             }
8388             return this;
8389         },
8390
8391         // private
8392         fixDisplay : function(){
8393             if(this.getStyle("display") == "none"){
8394                 this.setStyle("visibility", "hidden");
8395                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8396                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8397                     this.setStyle("display", "block");
8398                 }
8399             }
8400         },
8401
8402         /**
8403          * Quick set left and top adding default units
8404          * @param {String} left The left CSS property value
8405          * @param {String} top The top CSS property value
8406          * @return {Roo.Element} this
8407          */
8408          setLeftTop : function(left, top){
8409             this.dom.style.left = this.addUnits(left);
8410             this.dom.style.top = this.addUnits(top);
8411             return this;
8412         },
8413
8414         /**
8415          * Move this element relative to its current position.
8416          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8417          * @param {Number} distance How far to move the element in pixels
8418          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8419          * @return {Roo.Element} this
8420          */
8421          move : function(direction, distance, animate){
8422             var xy = this.getXY();
8423             direction = direction.toLowerCase();
8424             switch(direction){
8425                 case "l":
8426                 case "left":
8427                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8428                     break;
8429                case "r":
8430                case "right":
8431                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8432                     break;
8433                case "t":
8434                case "top":
8435                case "up":
8436                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8437                     break;
8438                case "b":
8439                case "bottom":
8440                case "down":
8441                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8442                     break;
8443             }
8444             return this;
8445         },
8446
8447         /**
8448          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8449          * @return {Roo.Element} this
8450          */
8451         clip : function(){
8452             if(!this.isClipped){
8453                this.isClipped = true;
8454                this.originalClip = {
8455                    "o": this.getStyle("overflow"),
8456                    "x": this.getStyle("overflow-x"),
8457                    "y": this.getStyle("overflow-y")
8458                };
8459                this.setStyle("overflow", "hidden");
8460                this.setStyle("overflow-x", "hidden");
8461                this.setStyle("overflow-y", "hidden");
8462             }
8463             return this;
8464         },
8465
8466         /**
8467          *  Return clipping (overflow) to original clipping before clip() was called
8468          * @return {Roo.Element} this
8469          */
8470         unclip : function(){
8471             if(this.isClipped){
8472                 this.isClipped = false;
8473                 var o = this.originalClip;
8474                 if(o.o){this.setStyle("overflow", o.o);}
8475                 if(o.x){this.setStyle("overflow-x", o.x);}
8476                 if(o.y){this.setStyle("overflow-y", o.y);}
8477             }
8478             return this;
8479         },
8480
8481
8482         /**
8483          * Gets the x,y coordinates specified by the anchor position on the element.
8484          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8485          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8486          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8487          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8488          * @return {Array} [x, y] An array containing the element's x and y coordinates
8489          */
8490         getAnchorXY : function(anchor, local, s){
8491             //Passing a different size is useful for pre-calculating anchors,
8492             //especially for anchored animations that change the el size.
8493
8494             var w, h, vp = false;
8495             if(!s){
8496                 var d = this.dom;
8497                 if(d == document.body || d == document){
8498                     vp = true;
8499                     w = D.getViewWidth(); h = D.getViewHeight();
8500                 }else{
8501                     w = this.getWidth(); h = this.getHeight();
8502                 }
8503             }else{
8504                 w = s.width;  h = s.height;
8505             }
8506             var x = 0, y = 0, r = Math.round;
8507             switch((anchor || "tl").toLowerCase()){
8508                 case "c":
8509                     x = r(w*.5);
8510                     y = r(h*.5);
8511                 break;
8512                 case "t":
8513                     x = r(w*.5);
8514                     y = 0;
8515                 break;
8516                 case "l":
8517                     x = 0;
8518                     y = r(h*.5);
8519                 break;
8520                 case "r":
8521                     x = w;
8522                     y = r(h*.5);
8523                 break;
8524                 case "b":
8525                     x = r(w*.5);
8526                     y = h;
8527                 break;
8528                 case "tl":
8529                     x = 0;
8530                     y = 0;
8531                 break;
8532                 case "bl":
8533                     x = 0;
8534                     y = h;
8535                 break;
8536                 case "br":
8537                     x = w;
8538                     y = h;
8539                 break;
8540                 case "tr":
8541                     x = w;
8542                     y = 0;
8543                 break;
8544             }
8545             if(local === true){
8546                 return [x, y];
8547             }
8548             if(vp){
8549                 var sc = this.getScroll();
8550                 return [x + sc.left, y + sc.top];
8551             }
8552             //Add the element's offset xy
8553             var o = this.getXY();
8554             return [x+o[0], y+o[1]];
8555         },
8556
8557         /**
8558          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8559          * supported position values.
8560          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8561          * @param {String} position The position to align to.
8562          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8563          * @return {Array} [x, y]
8564          */
8565         getAlignToXY : function(el, p, o){
8566             el = Roo.get(el);
8567             var d = this.dom;
8568             if(!el.dom){
8569                 throw "Element.alignTo with an element that doesn't exist";
8570             }
8571             var c = false; //constrain to viewport
8572             var p1 = "", p2 = "";
8573             o = o || [0,0];
8574
8575             if(!p){
8576                 p = "tl-bl";
8577             }else if(p == "?"){
8578                 p = "tl-bl?";
8579             }else if(p.indexOf("-") == -1){
8580                 p = "tl-" + p;
8581             }
8582             p = p.toLowerCase();
8583             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8584             if(!m){
8585                throw "Element.alignTo with an invalid alignment " + p;
8586             }
8587             p1 = m[1]; p2 = m[2]; c = !!m[3];
8588
8589             //Subtract the aligned el's internal xy from the target's offset xy
8590             //plus custom offset to get the aligned el's new offset xy
8591             var a1 = this.getAnchorXY(p1, true);
8592             var a2 = el.getAnchorXY(p2, false);
8593             var x = a2[0] - a1[0] + o[0];
8594             var y = a2[1] - a1[1] + o[1];
8595             if(c){
8596                 //constrain the aligned el to viewport if necessary
8597                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8598                 // 5px of margin for ie
8599                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8600
8601                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8602                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8603                 //otherwise swap the aligned el to the opposite border of the target.
8604                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8605                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8606                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8607                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8608
8609                var doc = document;
8610                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8611                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8612
8613                if((x+w) > dw + scrollX){
8614                     x = swapX ? r.left-w : dw+scrollX-w;
8615                 }
8616                if(x < scrollX){
8617                    x = swapX ? r.right : scrollX;
8618                }
8619                if((y+h) > dh + scrollY){
8620                     y = swapY ? r.top-h : dh+scrollY-h;
8621                 }
8622                if (y < scrollY){
8623                    y = swapY ? r.bottom : scrollY;
8624                }
8625             }
8626             return [x,y];
8627         },
8628
8629         // private
8630         getConstrainToXY : function(){
8631             var os = {top:0, left:0, bottom:0, right: 0};
8632
8633             return function(el, local, offsets, proposedXY){
8634                 el = Roo.get(el);
8635                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8636
8637                 var vw, vh, vx = 0, vy = 0;
8638                 if(el.dom == document.body || el.dom == document){
8639                     vw = Roo.lib.Dom.getViewWidth();
8640                     vh = Roo.lib.Dom.getViewHeight();
8641                 }else{
8642                     vw = el.dom.clientWidth;
8643                     vh = el.dom.clientHeight;
8644                     if(!local){
8645                         var vxy = el.getXY();
8646                         vx = vxy[0];
8647                         vy = vxy[1];
8648                     }
8649                 }
8650
8651                 var s = el.getScroll();
8652
8653                 vx += offsets.left + s.left;
8654                 vy += offsets.top + s.top;
8655
8656                 vw -= offsets.right;
8657                 vh -= offsets.bottom;
8658
8659                 var vr = vx+vw;
8660                 var vb = vy+vh;
8661
8662                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8663                 var x = xy[0], y = xy[1];
8664                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8665
8666                 // only move it if it needs it
8667                 var moved = false;
8668
8669                 // first validate right/bottom
8670                 if((x + w) > vr){
8671                     x = vr - w;
8672                     moved = true;
8673                 }
8674                 if((y + h) > vb){
8675                     y = vb - h;
8676                     moved = true;
8677                 }
8678                 // then make sure top/left isn't negative
8679                 if(x < vx){
8680                     x = vx;
8681                     moved = true;
8682                 }
8683                 if(y < vy){
8684                     y = vy;
8685                     moved = true;
8686                 }
8687                 return moved ? [x, y] : false;
8688             };
8689         }(),
8690
8691         // private
8692         adjustForConstraints : function(xy, parent, offsets){
8693             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8694         },
8695
8696         /**
8697          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8698          * document it aligns it to the viewport.
8699          * The position parameter is optional, and can be specified in any one of the following formats:
8700          * <ul>
8701          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8702          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8703          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8704          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8705          *   <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
8706          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8707          * </ul>
8708          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8709          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8710          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8711          * that specified in order to enforce the viewport constraints.
8712          * Following are all of the supported anchor positions:
8713     <pre>
8714     Value  Description
8715     -----  -----------------------------
8716     tl     The top left corner (default)
8717     t      The center of the top edge
8718     tr     The top right corner
8719     l      The center of the left edge
8720     c      In the center of the element
8721     r      The center of the right edge
8722     bl     The bottom left corner
8723     b      The center of the bottom edge
8724     br     The bottom right corner
8725     </pre>
8726     Example Usage:
8727     <pre><code>
8728     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8729     el.alignTo("other-el");
8730
8731     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8732     el.alignTo("other-el", "tr?");
8733
8734     // align the bottom right corner of el with the center left edge of other-el
8735     el.alignTo("other-el", "br-l?");
8736
8737     // align the center of el with the bottom left corner of other-el and
8738     // adjust the x position by -6 pixels (and the y position by 0)
8739     el.alignTo("other-el", "c-bl", [-6, 0]);
8740     </code></pre>
8741          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8742          * @param {String} position The position to align to.
8743          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8744          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8745          * @return {Roo.Element} this
8746          */
8747         alignTo : function(element, position, offsets, animate){
8748             var xy = this.getAlignToXY(element, position, offsets);
8749             this.setXY(xy, this.preanim(arguments, 3));
8750             return this;
8751         },
8752
8753         /**
8754          * Anchors an element to another element and realigns it when the window is resized.
8755          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8756          * @param {String} position The position to align to.
8757          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8758          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8759          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8760          * is a number, it is used as the buffer delay (defaults to 50ms).
8761          * @param {Function} callback The function to call after the animation finishes
8762          * @return {Roo.Element} this
8763          */
8764         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8765             var action = function(){
8766                 this.alignTo(el, alignment, offsets, animate);
8767                 Roo.callback(callback, this);
8768             };
8769             Roo.EventManager.onWindowResize(action, this);
8770             var tm = typeof monitorScroll;
8771             if(tm != 'undefined'){
8772                 Roo.EventManager.on(window, 'scroll', action, this,
8773                     {buffer: tm == 'number' ? monitorScroll : 50});
8774             }
8775             action.call(this); // align immediately
8776             return this;
8777         },
8778         /**
8779          * Clears any opacity settings from this element. Required in some cases for IE.
8780          * @return {Roo.Element} this
8781          */
8782         clearOpacity : function(){
8783             if (window.ActiveXObject) {
8784                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8785                     this.dom.style.filter = "";
8786                 }
8787             } else {
8788                 this.dom.style.opacity = "";
8789                 this.dom.style["-moz-opacity"] = "";
8790                 this.dom.style["-khtml-opacity"] = "";
8791             }
8792             return this;
8793         },
8794
8795         /**
8796          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8797          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8798          * @return {Roo.Element} this
8799          */
8800         hide : function(animate){
8801             this.setVisible(false, this.preanim(arguments, 0));
8802             return this;
8803         },
8804
8805         /**
8806         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8807         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8808          * @return {Roo.Element} this
8809          */
8810         show : function(animate){
8811             this.setVisible(true, this.preanim(arguments, 0));
8812             return this;
8813         },
8814
8815         /**
8816          * @private Test if size has a unit, otherwise appends the default
8817          */
8818         addUnits : function(size){
8819             return Roo.Element.addUnits(size, this.defaultUnit);
8820         },
8821
8822         /**
8823          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8824          * @return {Roo.Element} this
8825          */
8826         beginMeasure : function(){
8827             var el = this.dom;
8828             if(el.offsetWidth || el.offsetHeight){
8829                 return this; // offsets work already
8830             }
8831             var changed = [];
8832             var p = this.dom, b = document.body; // start with this element
8833             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8834                 var pe = Roo.get(p);
8835                 if(pe.getStyle('display') == 'none'){
8836                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8837                     p.style.visibility = "hidden";
8838                     p.style.display = "block";
8839                 }
8840                 p = p.parentNode;
8841             }
8842             this._measureChanged = changed;
8843             return this;
8844
8845         },
8846
8847         /**
8848          * Restores displays to before beginMeasure was called
8849          * @return {Roo.Element} this
8850          */
8851         endMeasure : function(){
8852             var changed = this._measureChanged;
8853             if(changed){
8854                 for(var i = 0, len = changed.length; i < len; i++) {
8855                     var r = changed[i];
8856                     r.el.style.visibility = r.visibility;
8857                     r.el.style.display = "none";
8858                 }
8859                 this._measureChanged = null;
8860             }
8861             return this;
8862         },
8863
8864         /**
8865         * Update the innerHTML of this element, optionally searching for and processing scripts
8866         * @param {String} html The new HTML
8867         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8868         * @param {Function} callback For async script loading you can be noticed when the update completes
8869         * @return {Roo.Element} this
8870          */
8871         update : function(html, loadScripts, callback){
8872             if(typeof html == "undefined"){
8873                 html = "";
8874             }
8875             if(loadScripts !== true){
8876                 this.dom.innerHTML = html;
8877                 if(typeof callback == "function"){
8878                     callback();
8879                 }
8880                 return this;
8881             }
8882             var id = Roo.id();
8883             var dom = this.dom;
8884
8885             html += '<span id="' + id + '"></span>';
8886
8887             E.onAvailable(id, function(){
8888                 var hd = document.getElementsByTagName("head")[0];
8889                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8890                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8891                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8892
8893                 var match;
8894                 while(match = re.exec(html)){
8895                     var attrs = match[1];
8896                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8897                     if(srcMatch && srcMatch[2]){
8898                        var s = document.createElement("script");
8899                        s.src = srcMatch[2];
8900                        var typeMatch = attrs.match(typeRe);
8901                        if(typeMatch && typeMatch[2]){
8902                            s.type = typeMatch[2];
8903                        }
8904                        hd.appendChild(s);
8905                     }else if(match[2] && match[2].length > 0){
8906                         if(window.execScript) {
8907                            window.execScript(match[2]);
8908                         } else {
8909                             /**
8910                              * eval:var:id
8911                              * eval:var:dom
8912                              * eval:var:html
8913                              * 
8914                              */
8915                            window.eval(match[2]);
8916                         }
8917                     }
8918                 }
8919                 var el = document.getElementById(id);
8920                 if(el){el.parentNode.removeChild(el);}
8921                 if(typeof callback == "function"){
8922                     callback();
8923                 }
8924             });
8925             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8926             return this;
8927         },
8928
8929         /**
8930          * Direct access to the UpdateManager update() method (takes the same parameters).
8931          * @param {String/Function} url The url for this request or a function to call to get the url
8932          * @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}
8933          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8934          * @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.
8935          * @return {Roo.Element} this
8936          */
8937         load : function(){
8938             var um = this.getUpdateManager();
8939             um.update.apply(um, arguments);
8940             return this;
8941         },
8942
8943         /**
8944         * Gets this element's UpdateManager
8945         * @return {Roo.UpdateManager} The UpdateManager
8946         */
8947         getUpdateManager : function(){
8948             if(!this.updateManager){
8949                 this.updateManager = new Roo.UpdateManager(this);
8950             }
8951             return this.updateManager;
8952         },
8953
8954         /**
8955          * Disables text selection for this element (normalized across browsers)
8956          * @return {Roo.Element} this
8957          */
8958         unselectable : function(){
8959             this.dom.unselectable = "on";
8960             this.swallowEvent("selectstart", true);
8961             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8962             this.addClass("x-unselectable");
8963             return this;
8964         },
8965
8966         /**
8967         * Calculates the x, y to center this element on the screen
8968         * @return {Array} The x, y values [x, y]
8969         */
8970         getCenterXY : function(){
8971             return this.getAlignToXY(document, 'c-c');
8972         },
8973
8974         /**
8975         * Centers the Element in either the viewport, or another Element.
8976         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8977         */
8978         center : function(centerIn){
8979             this.alignTo(centerIn || document, 'c-c');
8980             return this;
8981         },
8982
8983         /**
8984          * Tests various css rules/browsers to determine if this element uses a border box
8985          * @return {Boolean}
8986          */
8987         isBorderBox : function(){
8988             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8989         },
8990
8991         /**
8992          * Return a box {x, y, width, height} that can be used to set another elements
8993          * size/location to match this element.
8994          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8995          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8996          * @return {Object} box An object in the format {x, y, width, height}
8997          */
8998         getBox : function(contentBox, local){
8999             var xy;
9000             if(!local){
9001                 xy = this.getXY();
9002             }else{
9003                 var left = parseInt(this.getStyle("left"), 10) || 0;
9004                 var top = parseInt(this.getStyle("top"), 10) || 0;
9005                 xy = [left, top];
9006             }
9007             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9008             if(!contentBox){
9009                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9010             }else{
9011                 var l = this.getBorderWidth("l")+this.getPadding("l");
9012                 var r = this.getBorderWidth("r")+this.getPadding("r");
9013                 var t = this.getBorderWidth("t")+this.getPadding("t");
9014                 var b = this.getBorderWidth("b")+this.getPadding("b");
9015                 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)};
9016             }
9017             bx.right = bx.x + bx.width;
9018             bx.bottom = bx.y + bx.height;
9019             return bx;
9020         },
9021
9022         /**
9023          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9024          for more information about the sides.
9025          * @param {String} sides
9026          * @return {Number}
9027          */
9028         getFrameWidth : function(sides, onlyContentBox){
9029             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9030         },
9031
9032         /**
9033          * 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.
9034          * @param {Object} box The box to fill {x, y, width, height}
9035          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9036          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9037          * @return {Roo.Element} this
9038          */
9039         setBox : function(box, adjust, animate){
9040             var w = box.width, h = box.height;
9041             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9042                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9043                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9044             }
9045             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9046             return this;
9047         },
9048
9049         /**
9050          * Forces the browser to repaint this element
9051          * @return {Roo.Element} this
9052          */
9053          repaint : function(){
9054             var dom = this.dom;
9055             this.addClass("x-repaint");
9056             setTimeout(function(){
9057                 Roo.get(dom).removeClass("x-repaint");
9058             }, 1);
9059             return this;
9060         },
9061
9062         /**
9063          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9064          * then it returns the calculated width of the sides (see getPadding)
9065          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9066          * @return {Object/Number}
9067          */
9068         getMargins : function(side){
9069             if(!side){
9070                 return {
9071                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9072                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9073                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9074                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9075                 };
9076             }else{
9077                 return this.addStyles(side, El.margins);
9078              }
9079         },
9080
9081         // private
9082         addStyles : function(sides, styles){
9083             var val = 0, v, w;
9084             for(var i = 0, len = sides.length; i < len; i++){
9085                 v = this.getStyle(styles[sides.charAt(i)]);
9086                 if(v){
9087                      w = parseInt(v, 10);
9088                      if(w){ val += w; }
9089                 }
9090             }
9091             return val;
9092         },
9093
9094         /**
9095          * Creates a proxy element of this element
9096          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9097          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9098          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9099          * @return {Roo.Element} The new proxy element
9100          */
9101         createProxy : function(config, renderTo, matchBox){
9102             if(renderTo){
9103                 renderTo = Roo.getDom(renderTo);
9104             }else{
9105                 renderTo = document.body;
9106             }
9107             config = typeof config == "object" ?
9108                 config : {tag : "div", cls: config};
9109             var proxy = Roo.DomHelper.append(renderTo, config, true);
9110             if(matchBox){
9111                proxy.setBox(this.getBox());
9112             }
9113             return proxy;
9114         },
9115
9116         /**
9117          * Puts a mask over this element to disable user interaction. Requires core.css.
9118          * This method can only be applied to elements which accept child nodes.
9119          * @param {String} msg (optional) A message to display in the mask
9120          * @param {String} msgCls (optional) A css class to apply to the msg element
9121          * @return {Element} The mask  element
9122          */
9123         mask : function(msg, msgCls)
9124         {
9125             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9126                 this.setStyle("position", "relative");
9127             }
9128             if(!this._mask){
9129                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9130             }
9131             
9132             this.addClass("x-masked");
9133             this._mask.setDisplayed(true);
9134             
9135             // we wander
9136             var z = 0;
9137             var dom = this.dom;
9138             while (dom && dom.style) {
9139                 if (!isNaN(parseInt(dom.style.zIndex))) {
9140                     z = Math.max(z, parseInt(dom.style.zIndex));
9141                 }
9142                 dom = dom.parentNode;
9143             }
9144             // if we are masking the body - then it hides everything..
9145             if (this.dom == document.body) {
9146                 z = 1000000;
9147                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9148                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9149             }
9150            
9151             if(typeof msg == 'string'){
9152                 if(!this._maskMsg){
9153                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9154                         cls: "roo-el-mask-msg", 
9155                         cn: [
9156                             {
9157                                 tag: 'i',
9158                                 cls: 'fa fa-spinner fa-spin'
9159                             },
9160                             {
9161                                 tag: 'div'
9162                             }   
9163                         ]
9164                     }, true);
9165                 }
9166                 var mm = this._maskMsg;
9167                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9168                 if (mm.dom.lastChild) { // weird IE issue?
9169                     mm.dom.lastChild.innerHTML = msg;
9170                 }
9171                 mm.setDisplayed(true);
9172                 mm.center(this);
9173                 mm.setStyle('z-index', z + 102);
9174             }
9175             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9176                 this._mask.setHeight(this.getHeight());
9177             }
9178             this._mask.setStyle('z-index', z + 100);
9179             
9180             return this._mask;
9181         },
9182
9183         /**
9184          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9185          * it is cached for reuse.
9186          */
9187         unmask : function(removeEl){
9188             if(this._mask){
9189                 if(removeEl === true){
9190                     this._mask.remove();
9191                     delete this._mask;
9192                     if(this._maskMsg){
9193                         this._maskMsg.remove();
9194                         delete this._maskMsg;
9195                     }
9196                 }else{
9197                     this._mask.setDisplayed(false);
9198                     if(this._maskMsg){
9199                         this._maskMsg.setDisplayed(false);
9200                     }
9201                 }
9202             }
9203             this.removeClass("x-masked");
9204         },
9205
9206         /**
9207          * Returns true if this element is masked
9208          * @return {Boolean}
9209          */
9210         isMasked : function(){
9211             return this._mask && this._mask.isVisible();
9212         },
9213
9214         /**
9215          * Creates an iframe shim for this element to keep selects and other windowed objects from
9216          * showing through.
9217          * @return {Roo.Element} The new shim element
9218          */
9219         createShim : function(){
9220             var el = document.createElement('iframe');
9221             el.frameBorder = 'no';
9222             el.className = 'roo-shim';
9223             if(Roo.isIE && Roo.isSecure){
9224                 el.src = Roo.SSL_SECURE_URL;
9225             }
9226             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9227             shim.autoBoxAdjust = false;
9228             return shim;
9229         },
9230
9231         /**
9232          * Removes this element from the DOM and deletes it from the cache
9233          */
9234         remove : function(){
9235             if(this.dom.parentNode){
9236                 this.dom.parentNode.removeChild(this.dom);
9237             }
9238             delete El.cache[this.dom.id];
9239         },
9240
9241         /**
9242          * Sets up event handlers to add and remove a css class when the mouse is over this element
9243          * @param {String} className
9244          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9245          * mouseout events for children elements
9246          * @return {Roo.Element} this
9247          */
9248         addClassOnOver : function(className, preventFlicker){
9249             this.on("mouseover", function(){
9250                 Roo.fly(this, '_internal').addClass(className);
9251             }, this.dom);
9252             var removeFn = function(e){
9253                 if(preventFlicker !== true || !e.within(this, true)){
9254                     Roo.fly(this, '_internal').removeClass(className);
9255                 }
9256             };
9257             this.on("mouseout", removeFn, this.dom);
9258             return this;
9259         },
9260
9261         /**
9262          * Sets up event handlers to add and remove a css class when this element has the focus
9263          * @param {String} className
9264          * @return {Roo.Element} this
9265          */
9266         addClassOnFocus : function(className){
9267             this.on("focus", function(){
9268                 Roo.fly(this, '_internal').addClass(className);
9269             }, this.dom);
9270             this.on("blur", function(){
9271                 Roo.fly(this, '_internal').removeClass(className);
9272             }, this.dom);
9273             return this;
9274         },
9275         /**
9276          * 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)
9277          * @param {String} className
9278          * @return {Roo.Element} this
9279          */
9280         addClassOnClick : function(className){
9281             var dom = this.dom;
9282             this.on("mousedown", function(){
9283                 Roo.fly(dom, '_internal').addClass(className);
9284                 var d = Roo.get(document);
9285                 var fn = function(){
9286                     Roo.fly(dom, '_internal').removeClass(className);
9287                     d.removeListener("mouseup", fn);
9288                 };
9289                 d.on("mouseup", fn);
9290             });
9291             return this;
9292         },
9293
9294         /**
9295          * Stops the specified event from bubbling and optionally prevents the default action
9296          * @param {String} eventName
9297          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9298          * @return {Roo.Element} this
9299          */
9300         swallowEvent : function(eventName, preventDefault){
9301             var fn = function(e){
9302                 e.stopPropagation();
9303                 if(preventDefault){
9304                     e.preventDefault();
9305                 }
9306             };
9307             if(eventName instanceof Array){
9308                 for(var i = 0, len = eventName.length; i < len; i++){
9309                      this.on(eventName[i], fn);
9310                 }
9311                 return this;
9312             }
9313             this.on(eventName, fn);
9314             return this;
9315         },
9316
9317         /**
9318          * @private
9319          */
9320       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9321
9322         /**
9323          * Sizes this element to its parent element's dimensions performing
9324          * neccessary box adjustments.
9325          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9326          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9327          * @return {Roo.Element} this
9328          */
9329         fitToParent : function(monitorResize, targetParent) {
9330           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9331           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9332           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9333             return;
9334           }
9335           var p = Roo.get(targetParent || this.dom.parentNode);
9336           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9337           if (monitorResize === true) {
9338             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9339             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9340           }
9341           return this;
9342         },
9343
9344         /**
9345          * Gets the next sibling, skipping text nodes
9346          * @return {HTMLElement} The next sibling or null
9347          */
9348         getNextSibling : function(){
9349             var n = this.dom.nextSibling;
9350             while(n && n.nodeType != 1){
9351                 n = n.nextSibling;
9352             }
9353             return n;
9354         },
9355
9356         /**
9357          * Gets the previous sibling, skipping text nodes
9358          * @return {HTMLElement} The previous sibling or null
9359          */
9360         getPrevSibling : function(){
9361             var n = this.dom.previousSibling;
9362             while(n && n.nodeType != 1){
9363                 n = n.previousSibling;
9364             }
9365             return n;
9366         },
9367
9368
9369         /**
9370          * Appends the passed element(s) to this element
9371          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9372          * @return {Roo.Element} this
9373          */
9374         appendChild: function(el){
9375             el = Roo.get(el);
9376             el.appendTo(this);
9377             return this;
9378         },
9379
9380         /**
9381          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9382          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9383          * automatically generated with the specified attributes.
9384          * @param {HTMLElement} insertBefore (optional) a child element of this element
9385          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9386          * @return {Roo.Element} The new child element
9387          */
9388         createChild: function(config, insertBefore, returnDom){
9389             config = config || {tag:'div'};
9390             if(insertBefore){
9391                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9392             }
9393             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9394         },
9395
9396         /**
9397          * Appends this element to the passed element
9398          * @param {String/HTMLElement/Element} el The new parent element
9399          * @return {Roo.Element} this
9400          */
9401         appendTo: function(el){
9402             el = Roo.getDom(el);
9403             el.appendChild(this.dom);
9404             return this;
9405         },
9406
9407         /**
9408          * Inserts this element before the passed element in the DOM
9409          * @param {String/HTMLElement/Element} el The element to insert before
9410          * @return {Roo.Element} this
9411          */
9412         insertBefore: function(el){
9413             el = Roo.getDom(el);
9414             el.parentNode.insertBefore(this.dom, el);
9415             return this;
9416         },
9417
9418         /**
9419          * Inserts this element after the passed element in the DOM
9420          * @param {String/HTMLElement/Element} el The element to insert after
9421          * @return {Roo.Element} this
9422          */
9423         insertAfter: function(el){
9424             el = Roo.getDom(el);
9425             el.parentNode.insertBefore(this.dom, el.nextSibling);
9426             return this;
9427         },
9428
9429         /**
9430          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9431          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9432          * @return {Roo.Element} The new child
9433          */
9434         insertFirst: function(el, returnDom){
9435             el = el || {};
9436             if(typeof el == 'object' && !el.nodeType){ // dh config
9437                 return this.createChild(el, this.dom.firstChild, returnDom);
9438             }else{
9439                 el = Roo.getDom(el);
9440                 this.dom.insertBefore(el, this.dom.firstChild);
9441                 return !returnDom ? Roo.get(el) : el;
9442             }
9443         },
9444
9445         /**
9446          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9447          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9448          * @param {String} where (optional) 'before' or 'after' defaults to before
9449          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9450          * @return {Roo.Element} the inserted Element
9451          */
9452         insertSibling: function(el, where, returnDom){
9453             where = where ? where.toLowerCase() : 'before';
9454             el = el || {};
9455             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9456
9457             if(typeof el == 'object' && !el.nodeType){ // dh config
9458                 if(where == 'after' && !this.dom.nextSibling){
9459                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9460                 }else{
9461                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9462                 }
9463
9464             }else{
9465                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9466                             where == 'before' ? this.dom : this.dom.nextSibling);
9467                 if(!returnDom){
9468                     rt = Roo.get(rt);
9469                 }
9470             }
9471             return rt;
9472         },
9473
9474         /**
9475          * Creates and wraps this element with another element
9476          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9477          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9478          * @return {HTMLElement/Element} The newly created wrapper element
9479          */
9480         wrap: function(config, returnDom){
9481             if(!config){
9482                 config = {tag: "div"};
9483             }
9484             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9485             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9486             return newEl;
9487         },
9488
9489         /**
9490          * Replaces the passed element with this element
9491          * @param {String/HTMLElement/Element} el The element to replace
9492          * @return {Roo.Element} this
9493          */
9494         replace: function(el){
9495             el = Roo.get(el);
9496             this.insertBefore(el);
9497             el.remove();
9498             return this;
9499         },
9500
9501         /**
9502          * Inserts an html fragment into this element
9503          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9504          * @param {String} html The HTML fragment
9505          * @param {Boolean} returnEl True to return an Roo.Element
9506          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9507          */
9508         insertHtml : function(where, html, returnEl){
9509             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9510             return returnEl ? Roo.get(el) : el;
9511         },
9512
9513         /**
9514          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9515          * @param {Object} o The object with the attributes
9516          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9517          * @return {Roo.Element} this
9518          */
9519         set : function(o, useSet){
9520             var el = this.dom;
9521             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9522             for(var attr in o){
9523                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9524                 if(attr=="cls"){
9525                     el.className = o["cls"];
9526                 }else{
9527                     if(useSet) {
9528                         el.setAttribute(attr, o[attr]);
9529                     } else {
9530                         el[attr] = o[attr];
9531                     }
9532                 }
9533             }
9534             if(o.style){
9535                 Roo.DomHelper.applyStyles(el, o.style);
9536             }
9537             return this;
9538         },
9539
9540         /**
9541          * Convenience method for constructing a KeyMap
9542          * @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:
9543          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9544          * @param {Function} fn The function to call
9545          * @param {Object} scope (optional) The scope of the function
9546          * @return {Roo.KeyMap} The KeyMap created
9547          */
9548         addKeyListener : function(key, fn, scope){
9549             var config;
9550             if(typeof key != "object" || key instanceof Array){
9551                 config = {
9552                     key: key,
9553                     fn: fn,
9554                     scope: scope
9555                 };
9556             }else{
9557                 config = {
9558                     key : key.key,
9559                     shift : key.shift,
9560                     ctrl : key.ctrl,
9561                     alt : key.alt,
9562                     fn: fn,
9563                     scope: scope
9564                 };
9565             }
9566             return new Roo.KeyMap(this, config);
9567         },
9568
9569         /**
9570          * Creates a KeyMap for this element
9571          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9572          * @return {Roo.KeyMap} The KeyMap created
9573          */
9574         addKeyMap : function(config){
9575             return new Roo.KeyMap(this, config);
9576         },
9577
9578         /**
9579          * Returns true if this element is scrollable.
9580          * @return {Boolean}
9581          */
9582          isScrollable : function(){
9583             var dom = this.dom;
9584             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9585         },
9586
9587         /**
9588          * 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().
9589          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9590          * @param {Number} value The new scroll value
9591          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9592          * @return {Element} this
9593          */
9594
9595         scrollTo : function(side, value, animate){
9596             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9597             if(!animate || !A){
9598                 this.dom[prop] = value;
9599             }else{
9600                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9601                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9602             }
9603             return this;
9604         },
9605
9606         /**
9607          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9608          * within this element's scrollable range.
9609          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9610          * @param {Number} distance How far to scroll the element in pixels
9611          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9612          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9613          * was scrolled as far as it could go.
9614          */
9615          scroll : function(direction, distance, animate){
9616              if(!this.isScrollable()){
9617                  return;
9618              }
9619              var el = this.dom;
9620              var l = el.scrollLeft, t = el.scrollTop;
9621              var w = el.scrollWidth, h = el.scrollHeight;
9622              var cw = el.clientWidth, ch = el.clientHeight;
9623              direction = direction.toLowerCase();
9624              var scrolled = false;
9625              var a = this.preanim(arguments, 2);
9626              switch(direction){
9627                  case "l":
9628                  case "left":
9629                      if(w - l > cw){
9630                          var v = Math.min(l + distance, w-cw);
9631                          this.scrollTo("left", v, a);
9632                          scrolled = true;
9633                      }
9634                      break;
9635                 case "r":
9636                 case "right":
9637                      if(l > 0){
9638                          var v = Math.max(l - distance, 0);
9639                          this.scrollTo("left", v, a);
9640                          scrolled = true;
9641                      }
9642                      break;
9643                 case "t":
9644                 case "top":
9645                 case "up":
9646                      if(t > 0){
9647                          var v = Math.max(t - distance, 0);
9648                          this.scrollTo("top", v, a);
9649                          scrolled = true;
9650                      }
9651                      break;
9652                 case "b":
9653                 case "bottom":
9654                 case "down":
9655                      if(h - t > ch){
9656                          var v = Math.min(t + distance, h-ch);
9657                          this.scrollTo("top", v, a);
9658                          scrolled = true;
9659                      }
9660                      break;
9661              }
9662              return scrolled;
9663         },
9664
9665         /**
9666          * Translates the passed page coordinates into left/top css values for this element
9667          * @param {Number/Array} x The page x or an array containing [x, y]
9668          * @param {Number} y The page y
9669          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9670          */
9671         translatePoints : function(x, y){
9672             if(typeof x == 'object' || x instanceof Array){
9673                 y = x[1]; x = x[0];
9674             }
9675             var p = this.getStyle('position');
9676             var o = this.getXY();
9677
9678             var l = parseInt(this.getStyle('left'), 10);
9679             var t = parseInt(this.getStyle('top'), 10);
9680
9681             if(isNaN(l)){
9682                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9683             }
9684             if(isNaN(t)){
9685                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9686             }
9687
9688             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9689         },
9690
9691         /**
9692          * Returns the current scroll position of the element.
9693          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9694          */
9695         getScroll : function(){
9696             var d = this.dom, doc = document;
9697             if(d == doc || d == doc.body){
9698                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9699                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9700                 return {left: l, top: t};
9701             }else{
9702                 return {left: d.scrollLeft, top: d.scrollTop};
9703             }
9704         },
9705
9706         /**
9707          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9708          * are convert to standard 6 digit hex color.
9709          * @param {String} attr The css attribute
9710          * @param {String} defaultValue The default value to use when a valid color isn't found
9711          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9712          * YUI color anims.
9713          */
9714         getColor : function(attr, defaultValue, prefix){
9715             var v = this.getStyle(attr);
9716             if(!v || v == "transparent" || v == "inherit") {
9717                 return defaultValue;
9718             }
9719             var color = typeof prefix == "undefined" ? "#" : prefix;
9720             if(v.substr(0, 4) == "rgb("){
9721                 var rvs = v.slice(4, v.length -1).split(",");
9722                 for(var i = 0; i < 3; i++){
9723                     var h = parseInt(rvs[i]).toString(16);
9724                     if(h < 16){
9725                         h = "0" + h;
9726                     }
9727                     color += h;
9728                 }
9729             } else {
9730                 if(v.substr(0, 1) == "#"){
9731                     if(v.length == 4) {
9732                         for(var i = 1; i < 4; i++){
9733                             var c = v.charAt(i);
9734                             color +=  c + c;
9735                         }
9736                     }else if(v.length == 7){
9737                         color += v.substr(1);
9738                     }
9739                 }
9740             }
9741             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9742         },
9743
9744         /**
9745          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9746          * gradient background, rounded corners and a 4-way shadow.
9747          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9748          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9749          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9750          * @return {Roo.Element} this
9751          */
9752         boxWrap : function(cls){
9753             cls = cls || 'x-box';
9754             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9755             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9756             return el;
9757         },
9758
9759         /**
9760          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9761          * @param {String} namespace The namespace in which to look for the attribute
9762          * @param {String} name The attribute name
9763          * @return {String} The attribute value
9764          */
9765         getAttributeNS : Roo.isIE ? function(ns, name){
9766             var d = this.dom;
9767             var type = typeof d[ns+":"+name];
9768             if(type != 'undefined' && type != 'unknown'){
9769                 return d[ns+":"+name];
9770             }
9771             return d[name];
9772         } : function(ns, name){
9773             var d = this.dom;
9774             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9775         },
9776         
9777         
9778         /**
9779          * Sets or Returns the value the dom attribute value
9780          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9781          * @param {String} value (optional) The value to set the attribute to
9782          * @return {String} The attribute value
9783          */
9784         attr : function(name){
9785             if (arguments.length > 1) {
9786                 this.dom.setAttribute(name, arguments[1]);
9787                 return arguments[1];
9788             }
9789             if (typeof(name) == 'object') {
9790                 for(var i in name) {
9791                     this.attr(i, name[i]);
9792                 }
9793                 return name;
9794             }
9795             
9796             
9797             if (!this.dom.hasAttribute(name)) {
9798                 return undefined;
9799             }
9800             return this.dom.getAttribute(name);
9801         }
9802         
9803         
9804         
9805     };
9806
9807     var ep = El.prototype;
9808
9809     /**
9810      * Appends an event handler (Shorthand for addListener)
9811      * @param {String}   eventName     The type of event to append
9812      * @param {Function} fn        The method the event invokes
9813      * @param {Object} scope       (optional) The scope (this object) of the fn
9814      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9815      * @method
9816      */
9817     ep.on = ep.addListener;
9818         // backwards compat
9819     ep.mon = ep.addListener;
9820
9821     /**
9822      * Removes an event handler from this element (shorthand for removeListener)
9823      * @param {String} eventName the type of event to remove
9824      * @param {Function} fn the method the event invokes
9825      * @return {Roo.Element} this
9826      * @method
9827      */
9828     ep.un = ep.removeListener;
9829
9830     /**
9831      * true to automatically adjust width and height settings for box-model issues (default to true)
9832      */
9833     ep.autoBoxAdjust = true;
9834
9835     // private
9836     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9837
9838     // private
9839     El.addUnits = function(v, defaultUnit){
9840         if(v === "" || v == "auto"){
9841             return v;
9842         }
9843         if(v === undefined){
9844             return '';
9845         }
9846         if(typeof v == "number" || !El.unitPattern.test(v)){
9847             return v + (defaultUnit || 'px');
9848         }
9849         return v;
9850     };
9851
9852     // special markup used throughout Roo when box wrapping elements
9853     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>';
9854     /**
9855      * Visibility mode constant - Use visibility to hide element
9856      * @static
9857      * @type Number
9858      */
9859     El.VISIBILITY = 1;
9860     /**
9861      * Visibility mode constant - Use display to hide element
9862      * @static
9863      * @type Number
9864      */
9865     El.DISPLAY = 2;
9866
9867     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9868     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9869     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9870
9871
9872
9873     /**
9874      * @private
9875      */
9876     El.cache = {};
9877
9878     var docEl;
9879
9880     /**
9881      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9882      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9883      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9884      * @return {Element} The Element object
9885      * @static
9886      */
9887     El.get = function(el){
9888         var ex, elm, id;
9889         if(!el){ return null; }
9890         if(typeof el == "string"){ // element id
9891             if(!(elm = document.getElementById(el))){
9892                 return null;
9893             }
9894             if(ex = El.cache[el]){
9895                 ex.dom = elm;
9896             }else{
9897                 ex = El.cache[el] = new El(elm);
9898             }
9899             return ex;
9900         }else if(el.tagName){ // dom element
9901             if(!(id = el.id)){
9902                 id = Roo.id(el);
9903             }
9904             if(ex = El.cache[id]){
9905                 ex.dom = el;
9906             }else{
9907                 ex = El.cache[id] = new El(el);
9908             }
9909             return ex;
9910         }else if(el instanceof El){
9911             if(el != docEl){
9912                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9913                                                               // catch case where it hasn't been appended
9914                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9915             }
9916             return el;
9917         }else if(el.isComposite){
9918             return el;
9919         }else if(el instanceof Array){
9920             return El.select(el);
9921         }else if(el == document){
9922             // create a bogus element object representing the document object
9923             if(!docEl){
9924                 var f = function(){};
9925                 f.prototype = El.prototype;
9926                 docEl = new f();
9927                 docEl.dom = document;
9928             }
9929             return docEl;
9930         }
9931         return null;
9932     };
9933
9934     // private
9935     El.uncache = function(el){
9936         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9937             if(a[i]){
9938                 delete El.cache[a[i].id || a[i]];
9939             }
9940         }
9941     };
9942
9943     // private
9944     // Garbage collection - uncache elements/purge listeners on orphaned elements
9945     // so we don't hold a reference and cause the browser to retain them
9946     El.garbageCollect = function(){
9947         if(!Roo.enableGarbageCollector){
9948             clearInterval(El.collectorThread);
9949             return;
9950         }
9951         for(var eid in El.cache){
9952             var el = El.cache[eid], d = el.dom;
9953             // -------------------------------------------------------
9954             // Determining what is garbage:
9955             // -------------------------------------------------------
9956             // !d
9957             // dom node is null, definitely garbage
9958             // -------------------------------------------------------
9959             // !d.parentNode
9960             // no parentNode == direct orphan, definitely garbage
9961             // -------------------------------------------------------
9962             // !d.offsetParent && !document.getElementById(eid)
9963             // display none elements have no offsetParent so we will
9964             // also try to look it up by it's id. However, check
9965             // offsetParent first so we don't do unneeded lookups.
9966             // This enables collection of elements that are not orphans
9967             // directly, but somewhere up the line they have an orphan
9968             // parent.
9969             // -------------------------------------------------------
9970             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9971                 delete El.cache[eid];
9972                 if(d && Roo.enableListenerCollection){
9973                     E.purgeElement(d);
9974                 }
9975             }
9976         }
9977     }
9978     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9979
9980
9981     // dom is optional
9982     El.Flyweight = function(dom){
9983         this.dom = dom;
9984     };
9985     El.Flyweight.prototype = El.prototype;
9986
9987     El._flyweights = {};
9988     /**
9989      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9990      * the dom node can be overwritten by other code.
9991      * @param {String/HTMLElement} el The dom node or id
9992      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9993      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9994      * @static
9995      * @return {Element} The shared Element object
9996      */
9997     El.fly = function(el, named){
9998         named = named || '_global';
9999         el = Roo.getDom(el);
10000         if(!el){
10001             return null;
10002         }
10003         if(!El._flyweights[named]){
10004             El._flyweights[named] = new El.Flyweight();
10005         }
10006         El._flyweights[named].dom = el;
10007         return El._flyweights[named];
10008     };
10009
10010     /**
10011      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10012      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10013      * Shorthand of {@link Roo.Element#get}
10014      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10015      * @return {Element} The Element object
10016      * @member Roo
10017      * @method get
10018      */
10019     Roo.get = El.get;
10020     /**
10021      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10022      * the dom node can be overwritten by other code.
10023      * Shorthand of {@link Roo.Element#fly}
10024      * @param {String/HTMLElement} el The dom node or id
10025      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10026      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10027      * @static
10028      * @return {Element} The shared Element object
10029      * @member Roo
10030      * @method fly
10031      */
10032     Roo.fly = El.fly;
10033
10034     // speedy lookup for elements never to box adjust
10035     var noBoxAdjust = Roo.isStrict ? {
10036         select:1
10037     } : {
10038         input:1, select:1, textarea:1
10039     };
10040     if(Roo.isIE || Roo.isGecko){
10041         noBoxAdjust['button'] = 1;
10042     }
10043
10044
10045     Roo.EventManager.on(window, 'unload', function(){
10046         delete El.cache;
10047         delete El._flyweights;
10048     });
10049 })();
10050
10051
10052
10053
10054 if(Roo.DomQuery){
10055     Roo.Element.selectorFunction = Roo.DomQuery.select;
10056 }
10057
10058 Roo.Element.select = function(selector, unique, root){
10059     var els;
10060     if(typeof selector == "string"){
10061         els = Roo.Element.selectorFunction(selector, root);
10062     }else if(selector.length !== undefined){
10063         els = selector;
10064     }else{
10065         throw "Invalid selector";
10066     }
10067     if(unique === true){
10068         return new Roo.CompositeElement(els);
10069     }else{
10070         return new Roo.CompositeElementLite(els);
10071     }
10072 };
10073 /**
10074  * Selects elements based on the passed CSS selector to enable working on them as 1.
10075  * @param {String/Array} selector The CSS selector or an array of elements
10076  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10077  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10078  * @return {CompositeElementLite/CompositeElement}
10079  * @member Roo
10080  * @method select
10081  */
10082 Roo.select = Roo.Element.select;
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094
10095
10096
10097 /*
10098  * Based on:
10099  * Ext JS Library 1.1.1
10100  * Copyright(c) 2006-2007, Ext JS, LLC.
10101  *
10102  * Originally Released Under LGPL - original licence link has changed is not relivant.
10103  *
10104  * Fork - LGPL
10105  * <script type="text/javascript">
10106  */
10107
10108
10109
10110 //Notifies Element that fx methods are available
10111 Roo.enableFx = true;
10112
10113 /**
10114  * @class Roo.Fx
10115  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10116  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10117  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10118  * Element effects to work.</p><br/>
10119  *
10120  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10121  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10122  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10123  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10124  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10125  * expected results and should be done with care.</p><br/>
10126  *
10127  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10128  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10129 <pre>
10130 Value  Description
10131 -----  -----------------------------
10132 tl     The top left corner
10133 t      The center of the top edge
10134 tr     The top right corner
10135 l      The center of the left edge
10136 r      The center of the right edge
10137 bl     The bottom left corner
10138 b      The center of the bottom edge
10139 br     The bottom right corner
10140 </pre>
10141  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10142  * below are common options that can be passed to any Fx method.</b>
10143  * @cfg {Function} callback A function called when the effect is finished
10144  * @cfg {Object} scope The scope of the effect function
10145  * @cfg {String} easing A valid Easing value for the effect
10146  * @cfg {String} afterCls A css class to apply after the effect
10147  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10148  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10149  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10150  * effects that end with the element being visually hidden, ignored otherwise)
10151  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10152  * a function which returns such a specification that will be applied to the Element after the effect finishes
10153  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10154  * @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
10155  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10156  */
10157 Roo.Fx = {
10158         /**
10159          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10160          * origin for the slide effect.  This function automatically handles wrapping the element with
10161          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10162          * Usage:
10163          *<pre><code>
10164 // default: slide the element in from the top
10165 el.slideIn();
10166
10167 // custom: slide the element in from the right with a 2-second duration
10168 el.slideIn('r', { duration: 2 });
10169
10170 // common config options shown with default values
10171 el.slideIn('t', {
10172     easing: 'easeOut',
10173     duration: .5
10174 });
10175 </code></pre>
10176          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10177          * @param {Object} options (optional) Object literal with any of the Fx config options
10178          * @return {Roo.Element} The Element
10179          */
10180     slideIn : function(anchor, o){
10181         var el = this.getFxEl();
10182         o = o || {};
10183
10184         el.queueFx(o, function(){
10185
10186             anchor = anchor || "t";
10187
10188             // fix display to visibility
10189             this.fixDisplay();
10190
10191             // restore values after effect
10192             var r = this.getFxRestore();
10193             var b = this.getBox();
10194             // fixed size for slide
10195             this.setSize(b);
10196
10197             // wrap if needed
10198             var wrap = this.fxWrap(r.pos, o, "hidden");
10199
10200             var st = this.dom.style;
10201             st.visibility = "visible";
10202             st.position = "absolute";
10203
10204             // clear out temp styles after slide and unwrap
10205             var after = function(){
10206                 el.fxUnwrap(wrap, r.pos, o);
10207                 st.width = r.width;
10208                 st.height = r.height;
10209                 el.afterFx(o);
10210             };
10211             // time to calc the positions
10212             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10213
10214             switch(anchor.toLowerCase()){
10215                 case "t":
10216                     wrap.setSize(b.width, 0);
10217                     st.left = st.bottom = "0";
10218                     a = {height: bh};
10219                 break;
10220                 case "l":
10221                     wrap.setSize(0, b.height);
10222                     st.right = st.top = "0";
10223                     a = {width: bw};
10224                 break;
10225                 case "r":
10226                     wrap.setSize(0, b.height);
10227                     wrap.setX(b.right);
10228                     st.left = st.top = "0";
10229                     a = {width: bw, points: pt};
10230                 break;
10231                 case "b":
10232                     wrap.setSize(b.width, 0);
10233                     wrap.setY(b.bottom);
10234                     st.left = st.top = "0";
10235                     a = {height: bh, points: pt};
10236                 break;
10237                 case "tl":
10238                     wrap.setSize(0, 0);
10239                     st.right = st.bottom = "0";
10240                     a = {width: bw, height: bh};
10241                 break;
10242                 case "bl":
10243                     wrap.setSize(0, 0);
10244                     wrap.setY(b.y+b.height);
10245                     st.right = st.top = "0";
10246                     a = {width: bw, height: bh, points: pt};
10247                 break;
10248                 case "br":
10249                     wrap.setSize(0, 0);
10250                     wrap.setXY([b.right, b.bottom]);
10251                     st.left = st.top = "0";
10252                     a = {width: bw, height: bh, points: pt};
10253                 break;
10254                 case "tr":
10255                     wrap.setSize(0, 0);
10256                     wrap.setX(b.x+b.width);
10257                     st.left = st.bottom = "0";
10258                     a = {width: bw, height: bh, points: pt};
10259                 break;
10260             }
10261             this.dom.style.visibility = "visible";
10262             wrap.show();
10263
10264             arguments.callee.anim = wrap.fxanim(a,
10265                 o,
10266                 'motion',
10267                 .5,
10268                 'easeOut', after);
10269         });
10270         return this;
10271     },
10272     
10273         /**
10274          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10275          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10276          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10277          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10278          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10279          * Usage:
10280          *<pre><code>
10281 // default: slide the element out to the top
10282 el.slideOut();
10283
10284 // custom: slide the element out to the right with a 2-second duration
10285 el.slideOut('r', { duration: 2 });
10286
10287 // common config options shown with default values
10288 el.slideOut('t', {
10289     easing: 'easeOut',
10290     duration: .5,
10291     remove: false,
10292     useDisplay: false
10293 });
10294 </code></pre>
10295          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10296          * @param {Object} options (optional) Object literal with any of the Fx config options
10297          * @return {Roo.Element} The Element
10298          */
10299     slideOut : function(anchor, o){
10300         var el = this.getFxEl();
10301         o = o || {};
10302
10303         el.queueFx(o, function(){
10304
10305             anchor = anchor || "t";
10306
10307             // restore values after effect
10308             var r = this.getFxRestore();
10309             
10310             var b = this.getBox();
10311             // fixed size for slide
10312             this.setSize(b);
10313
10314             // wrap if needed
10315             var wrap = this.fxWrap(r.pos, o, "visible");
10316
10317             var st = this.dom.style;
10318             st.visibility = "visible";
10319             st.position = "absolute";
10320
10321             wrap.setSize(b);
10322
10323             var after = function(){
10324                 if(o.useDisplay){
10325                     el.setDisplayed(false);
10326                 }else{
10327                     el.hide();
10328                 }
10329
10330                 el.fxUnwrap(wrap, r.pos, o);
10331
10332                 st.width = r.width;
10333                 st.height = r.height;
10334
10335                 el.afterFx(o);
10336             };
10337
10338             var a, zero = {to: 0};
10339             switch(anchor.toLowerCase()){
10340                 case "t":
10341                     st.left = st.bottom = "0";
10342                     a = {height: zero};
10343                 break;
10344                 case "l":
10345                     st.right = st.top = "0";
10346                     a = {width: zero};
10347                 break;
10348                 case "r":
10349                     st.left = st.top = "0";
10350                     a = {width: zero, points: {to:[b.right, b.y]}};
10351                 break;
10352                 case "b":
10353                     st.left = st.top = "0";
10354                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10355                 break;
10356                 case "tl":
10357                     st.right = st.bottom = "0";
10358                     a = {width: zero, height: zero};
10359                 break;
10360                 case "bl":
10361                     st.right = st.top = "0";
10362                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10363                 break;
10364                 case "br":
10365                     st.left = st.top = "0";
10366                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10367                 break;
10368                 case "tr":
10369                     st.left = st.bottom = "0";
10370                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10371                 break;
10372             }
10373
10374             arguments.callee.anim = wrap.fxanim(a,
10375                 o,
10376                 'motion',
10377                 .5,
10378                 "easeOut", after);
10379         });
10380         return this;
10381     },
10382
10383         /**
10384          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10385          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10386          * The element must be removed from the DOM using the 'remove' config option if desired.
10387          * Usage:
10388          *<pre><code>
10389 // default
10390 el.puff();
10391
10392 // common config options shown with default values
10393 el.puff({
10394     easing: 'easeOut',
10395     duration: .5,
10396     remove: false,
10397     useDisplay: false
10398 });
10399 </code></pre>
10400          * @param {Object} options (optional) Object literal with any of the Fx config options
10401          * @return {Roo.Element} The Element
10402          */
10403     puff : function(o){
10404         var el = this.getFxEl();
10405         o = o || {};
10406
10407         el.queueFx(o, function(){
10408             this.clearOpacity();
10409             this.show();
10410
10411             // restore values after effect
10412             var r = this.getFxRestore();
10413             var st = this.dom.style;
10414
10415             var after = function(){
10416                 if(o.useDisplay){
10417                     el.setDisplayed(false);
10418                 }else{
10419                     el.hide();
10420                 }
10421
10422                 el.clearOpacity();
10423
10424                 el.setPositioning(r.pos);
10425                 st.width = r.width;
10426                 st.height = r.height;
10427                 st.fontSize = '';
10428                 el.afterFx(o);
10429             };
10430
10431             var width = this.getWidth();
10432             var height = this.getHeight();
10433
10434             arguments.callee.anim = this.fxanim({
10435                     width : {to: this.adjustWidth(width * 2)},
10436                     height : {to: this.adjustHeight(height * 2)},
10437                     points : {by: [-(width * .5), -(height * .5)]},
10438                     opacity : {to: 0},
10439                     fontSize: {to:200, unit: "%"}
10440                 },
10441                 o,
10442                 'motion',
10443                 .5,
10444                 "easeOut", after);
10445         });
10446         return this;
10447     },
10448
10449         /**
10450          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10451          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10452          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10453          * Usage:
10454          *<pre><code>
10455 // default
10456 el.switchOff();
10457
10458 // all config options shown with default values
10459 el.switchOff({
10460     easing: 'easeIn',
10461     duration: .3,
10462     remove: false,
10463     useDisplay: false
10464 });
10465 </code></pre>
10466          * @param {Object} options (optional) Object literal with any of the Fx config options
10467          * @return {Roo.Element} The Element
10468          */
10469     switchOff : function(o){
10470         var el = this.getFxEl();
10471         o = o || {};
10472
10473         el.queueFx(o, function(){
10474             this.clearOpacity();
10475             this.clip();
10476
10477             // restore values after effect
10478             var r = this.getFxRestore();
10479             var st = this.dom.style;
10480
10481             var after = function(){
10482                 if(o.useDisplay){
10483                     el.setDisplayed(false);
10484                 }else{
10485                     el.hide();
10486                 }
10487
10488                 el.clearOpacity();
10489                 el.setPositioning(r.pos);
10490                 st.width = r.width;
10491                 st.height = r.height;
10492
10493                 el.afterFx(o);
10494             };
10495
10496             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10497                 this.clearOpacity();
10498                 (function(){
10499                     this.fxanim({
10500                         height:{to:1},
10501                         points:{by:[0, this.getHeight() * .5]}
10502                     }, o, 'motion', 0.3, 'easeIn', after);
10503                 }).defer(100, this);
10504             });
10505         });
10506         return this;
10507     },
10508
10509     /**
10510      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10511      * changed using the "attr" config option) and then fading back to the original color. If no original
10512      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10513      * Usage:
10514 <pre><code>
10515 // default: highlight background to yellow
10516 el.highlight();
10517
10518 // custom: highlight foreground text to blue for 2 seconds
10519 el.highlight("0000ff", { attr: 'color', duration: 2 });
10520
10521 // common config options shown with default values
10522 el.highlight("ffff9c", {
10523     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10524     endColor: (current color) or "ffffff",
10525     easing: 'easeIn',
10526     duration: 1
10527 });
10528 </code></pre>
10529      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10530      * @param {Object} options (optional) Object literal with any of the Fx config options
10531      * @return {Roo.Element} The Element
10532      */ 
10533     highlight : function(color, o){
10534         var el = this.getFxEl();
10535         o = o || {};
10536
10537         el.queueFx(o, function(){
10538             color = color || "ffff9c";
10539             attr = o.attr || "backgroundColor";
10540
10541             this.clearOpacity();
10542             this.show();
10543
10544             var origColor = this.getColor(attr);
10545             var restoreColor = this.dom.style[attr];
10546             endColor = (o.endColor || origColor) || "ffffff";
10547
10548             var after = function(){
10549                 el.dom.style[attr] = restoreColor;
10550                 el.afterFx(o);
10551             };
10552
10553             var a = {};
10554             a[attr] = {from: color, to: endColor};
10555             arguments.callee.anim = this.fxanim(a,
10556                 o,
10557                 'color',
10558                 1,
10559                 'easeIn', after);
10560         });
10561         return this;
10562     },
10563
10564    /**
10565     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10566     * Usage:
10567 <pre><code>
10568 // default: a single light blue ripple
10569 el.frame();
10570
10571 // custom: 3 red ripples lasting 3 seconds total
10572 el.frame("ff0000", 3, { duration: 3 });
10573
10574 // common config options shown with default values
10575 el.frame("C3DAF9", 1, {
10576     duration: 1 //duration of entire animation (not each individual ripple)
10577     // Note: Easing is not configurable and will be ignored if included
10578 });
10579 </code></pre>
10580     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10581     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10582     * @param {Object} options (optional) Object literal with any of the Fx config options
10583     * @return {Roo.Element} The Element
10584     */
10585     frame : function(color, count, o){
10586         var el = this.getFxEl();
10587         o = o || {};
10588
10589         el.queueFx(o, function(){
10590             color = color || "#C3DAF9";
10591             if(color.length == 6){
10592                 color = "#" + color;
10593             }
10594             count = count || 1;
10595             duration = o.duration || 1;
10596             this.show();
10597
10598             var b = this.getBox();
10599             var animFn = function(){
10600                 var proxy = this.createProxy({
10601
10602                      style:{
10603                         visbility:"hidden",
10604                         position:"absolute",
10605                         "z-index":"35000", // yee haw
10606                         border:"0px solid " + color
10607                      }
10608                   });
10609                 var scale = Roo.isBorderBox ? 2 : 1;
10610                 proxy.animate({
10611                     top:{from:b.y, to:b.y - 20},
10612                     left:{from:b.x, to:b.x - 20},
10613                     borderWidth:{from:0, to:10},
10614                     opacity:{from:1, to:0},
10615                     height:{from:b.height, to:(b.height + (20*scale))},
10616                     width:{from:b.width, to:(b.width + (20*scale))}
10617                 }, duration, function(){
10618                     proxy.remove();
10619                 });
10620                 if(--count > 0){
10621                      animFn.defer((duration/2)*1000, this);
10622                 }else{
10623                     el.afterFx(o);
10624                 }
10625             };
10626             animFn.call(this);
10627         });
10628         return this;
10629     },
10630
10631    /**
10632     * Creates a pause before any subsequent queued effects begin.  If there are
10633     * no effects queued after the pause it will have no effect.
10634     * Usage:
10635 <pre><code>
10636 el.pause(1);
10637 </code></pre>
10638     * @param {Number} seconds The length of time to pause (in seconds)
10639     * @return {Roo.Element} The Element
10640     */
10641     pause : function(seconds){
10642         var el = this.getFxEl();
10643         var o = {};
10644
10645         el.queueFx(o, function(){
10646             setTimeout(function(){
10647                 el.afterFx(o);
10648             }, seconds * 1000);
10649         });
10650         return this;
10651     },
10652
10653    /**
10654     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10655     * using the "endOpacity" config option.
10656     * Usage:
10657 <pre><code>
10658 // default: fade in from opacity 0 to 100%
10659 el.fadeIn();
10660
10661 // custom: fade in from opacity 0 to 75% over 2 seconds
10662 el.fadeIn({ endOpacity: .75, duration: 2});
10663
10664 // common config options shown with default values
10665 el.fadeIn({
10666     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10667     easing: 'easeOut',
10668     duration: .5
10669 });
10670 </code></pre>
10671     * @param {Object} options (optional) Object literal with any of the Fx config options
10672     * @return {Roo.Element} The Element
10673     */
10674     fadeIn : function(o){
10675         var el = this.getFxEl();
10676         o = o || {};
10677         el.queueFx(o, function(){
10678             this.setOpacity(0);
10679             this.fixDisplay();
10680             this.dom.style.visibility = 'visible';
10681             var to = o.endOpacity || 1;
10682             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10683                 o, null, .5, "easeOut", function(){
10684                 if(to == 1){
10685                     this.clearOpacity();
10686                 }
10687                 el.afterFx(o);
10688             });
10689         });
10690         return this;
10691     },
10692
10693    /**
10694     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10695     * using the "endOpacity" config option.
10696     * Usage:
10697 <pre><code>
10698 // default: fade out from the element's current opacity to 0
10699 el.fadeOut();
10700
10701 // custom: fade out from the element's current opacity to 25% over 2 seconds
10702 el.fadeOut({ endOpacity: .25, duration: 2});
10703
10704 // common config options shown with default values
10705 el.fadeOut({
10706     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10707     easing: 'easeOut',
10708     duration: .5
10709     remove: false,
10710     useDisplay: false
10711 });
10712 </code></pre>
10713     * @param {Object} options (optional) Object literal with any of the Fx config options
10714     * @return {Roo.Element} The Element
10715     */
10716     fadeOut : function(o){
10717         var el = this.getFxEl();
10718         o = o || {};
10719         el.queueFx(o, function(){
10720             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10721                 o, null, .5, "easeOut", function(){
10722                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10723                      this.dom.style.display = "none";
10724                 }else{
10725                      this.dom.style.visibility = "hidden";
10726                 }
10727                 this.clearOpacity();
10728                 el.afterFx(o);
10729             });
10730         });
10731         return this;
10732     },
10733
10734    /**
10735     * Animates the transition of an element's dimensions from a starting height/width
10736     * to an ending height/width.
10737     * Usage:
10738 <pre><code>
10739 // change height and width to 100x100 pixels
10740 el.scale(100, 100);
10741
10742 // common config options shown with default values.  The height and width will default to
10743 // the element's existing values if passed as null.
10744 el.scale(
10745     [element's width],
10746     [element's height], {
10747     easing: 'easeOut',
10748     duration: .35
10749 });
10750 </code></pre>
10751     * @param {Number} width  The new width (pass undefined to keep the original width)
10752     * @param {Number} height  The new height (pass undefined to keep the original height)
10753     * @param {Object} options (optional) Object literal with any of the Fx config options
10754     * @return {Roo.Element} The Element
10755     */
10756     scale : function(w, h, o){
10757         this.shift(Roo.apply({}, o, {
10758             width: w,
10759             height: h
10760         }));
10761         return this;
10762     },
10763
10764    /**
10765     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10766     * Any of these properties not specified in the config object will not be changed.  This effect 
10767     * requires that at least one new dimension, position or opacity setting must be passed in on
10768     * the config object in order for the function to have any effect.
10769     * Usage:
10770 <pre><code>
10771 // slide the element horizontally to x position 200 while changing the height and opacity
10772 el.shift({ x: 200, height: 50, opacity: .8 });
10773
10774 // common config options shown with default values.
10775 el.shift({
10776     width: [element's width],
10777     height: [element's height],
10778     x: [element's x position],
10779     y: [element's y position],
10780     opacity: [element's opacity],
10781     easing: 'easeOut',
10782     duration: .35
10783 });
10784 </code></pre>
10785     * @param {Object} options  Object literal with any of the Fx config options
10786     * @return {Roo.Element} The Element
10787     */
10788     shift : function(o){
10789         var el = this.getFxEl();
10790         o = o || {};
10791         el.queueFx(o, function(){
10792             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10793             if(w !== undefined){
10794                 a.width = {to: this.adjustWidth(w)};
10795             }
10796             if(h !== undefined){
10797                 a.height = {to: this.adjustHeight(h)};
10798             }
10799             if(x !== undefined || y !== undefined){
10800                 a.points = {to: [
10801                     x !== undefined ? x : this.getX(),
10802                     y !== undefined ? y : this.getY()
10803                 ]};
10804             }
10805             if(op !== undefined){
10806                 a.opacity = {to: op};
10807             }
10808             if(o.xy !== undefined){
10809                 a.points = {to: o.xy};
10810             }
10811             arguments.callee.anim = this.fxanim(a,
10812                 o, 'motion', .35, "easeOut", function(){
10813                 el.afterFx(o);
10814             });
10815         });
10816         return this;
10817     },
10818
10819         /**
10820          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10821          * ending point of the effect.
10822          * Usage:
10823          *<pre><code>
10824 // default: slide the element downward while fading out
10825 el.ghost();
10826
10827 // custom: slide the element out to the right with a 2-second duration
10828 el.ghost('r', { duration: 2 });
10829
10830 // common config options shown with default values
10831 el.ghost('b', {
10832     easing: 'easeOut',
10833     duration: .5
10834     remove: false,
10835     useDisplay: false
10836 });
10837 </code></pre>
10838          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10839          * @param {Object} options (optional) Object literal with any of the Fx config options
10840          * @return {Roo.Element} The Element
10841          */
10842     ghost : function(anchor, o){
10843         var el = this.getFxEl();
10844         o = o || {};
10845
10846         el.queueFx(o, function(){
10847             anchor = anchor || "b";
10848
10849             // restore values after effect
10850             var r = this.getFxRestore();
10851             var w = this.getWidth(),
10852                 h = this.getHeight();
10853
10854             var st = this.dom.style;
10855
10856             var after = function(){
10857                 if(o.useDisplay){
10858                     el.setDisplayed(false);
10859                 }else{
10860                     el.hide();
10861                 }
10862
10863                 el.clearOpacity();
10864                 el.setPositioning(r.pos);
10865                 st.width = r.width;
10866                 st.height = r.height;
10867
10868                 el.afterFx(o);
10869             };
10870
10871             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10872             switch(anchor.toLowerCase()){
10873                 case "t":
10874                     pt.by = [0, -h];
10875                 break;
10876                 case "l":
10877                     pt.by = [-w, 0];
10878                 break;
10879                 case "r":
10880                     pt.by = [w, 0];
10881                 break;
10882                 case "b":
10883                     pt.by = [0, h];
10884                 break;
10885                 case "tl":
10886                     pt.by = [-w, -h];
10887                 break;
10888                 case "bl":
10889                     pt.by = [-w, h];
10890                 break;
10891                 case "br":
10892                     pt.by = [w, h];
10893                 break;
10894                 case "tr":
10895                     pt.by = [w, -h];
10896                 break;
10897             }
10898
10899             arguments.callee.anim = this.fxanim(a,
10900                 o,
10901                 'motion',
10902                 .5,
10903                 "easeOut", after);
10904         });
10905         return this;
10906     },
10907
10908         /**
10909          * Ensures that all effects queued after syncFx is called on the element are
10910          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10911          * @return {Roo.Element} The Element
10912          */
10913     syncFx : function(){
10914         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10915             block : false,
10916             concurrent : true,
10917             stopFx : false
10918         });
10919         return this;
10920     },
10921
10922         /**
10923          * Ensures that all effects queued after sequenceFx is called on the element are
10924          * run in sequence.  This is the opposite of {@link #syncFx}.
10925          * @return {Roo.Element} The Element
10926          */
10927     sequenceFx : function(){
10928         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10929             block : false,
10930             concurrent : false,
10931             stopFx : false
10932         });
10933         return this;
10934     },
10935
10936         /* @private */
10937     nextFx : function(){
10938         var ef = this.fxQueue[0];
10939         if(ef){
10940             ef.call(this);
10941         }
10942     },
10943
10944         /**
10945          * Returns true if the element has any effects actively running or queued, else returns false.
10946          * @return {Boolean} True if element has active effects, else false
10947          */
10948     hasActiveFx : function(){
10949         return this.fxQueue && this.fxQueue[0];
10950     },
10951
10952         /**
10953          * Stops any running effects and clears the element's internal effects queue if it contains
10954          * any additional effects that haven't started yet.
10955          * @return {Roo.Element} The Element
10956          */
10957     stopFx : function(){
10958         if(this.hasActiveFx()){
10959             var cur = this.fxQueue[0];
10960             if(cur && cur.anim && cur.anim.isAnimated()){
10961                 this.fxQueue = [cur]; // clear out others
10962                 cur.anim.stop(true);
10963             }
10964         }
10965         return this;
10966     },
10967
10968         /* @private */
10969     beforeFx : function(o){
10970         if(this.hasActiveFx() && !o.concurrent){
10971            if(o.stopFx){
10972                this.stopFx();
10973                return true;
10974            }
10975            return false;
10976         }
10977         return true;
10978     },
10979
10980         /**
10981          * Returns true if the element is currently blocking so that no other effect can be queued
10982          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10983          * used to ensure that an effect initiated by a user action runs to completion prior to the
10984          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10985          * @return {Boolean} True if blocking, else false
10986          */
10987     hasFxBlock : function(){
10988         var q = this.fxQueue;
10989         return q && q[0] && q[0].block;
10990     },
10991
10992         /* @private */
10993     queueFx : function(o, fn){
10994         if(!this.fxQueue){
10995             this.fxQueue = [];
10996         }
10997         if(!this.hasFxBlock()){
10998             Roo.applyIf(o, this.fxDefaults);
10999             if(!o.concurrent){
11000                 var run = this.beforeFx(o);
11001                 fn.block = o.block;
11002                 this.fxQueue.push(fn);
11003                 if(run){
11004                     this.nextFx();
11005                 }
11006             }else{
11007                 fn.call(this);
11008             }
11009         }
11010         return this;
11011     },
11012
11013         /* @private */
11014     fxWrap : function(pos, o, vis){
11015         var wrap;
11016         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11017             var wrapXY;
11018             if(o.fixPosition){
11019                 wrapXY = this.getXY();
11020             }
11021             var div = document.createElement("div");
11022             div.style.visibility = vis;
11023             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11024             wrap.setPositioning(pos);
11025             if(wrap.getStyle("position") == "static"){
11026                 wrap.position("relative");
11027             }
11028             this.clearPositioning('auto');
11029             wrap.clip();
11030             wrap.dom.appendChild(this.dom);
11031             if(wrapXY){
11032                 wrap.setXY(wrapXY);
11033             }
11034         }
11035         return wrap;
11036     },
11037
11038         /* @private */
11039     fxUnwrap : function(wrap, pos, o){
11040         this.clearPositioning();
11041         this.setPositioning(pos);
11042         if(!o.wrap){
11043             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11044             wrap.remove();
11045         }
11046     },
11047
11048         /* @private */
11049     getFxRestore : function(){
11050         var st = this.dom.style;
11051         return {pos: this.getPositioning(), width: st.width, height : st.height};
11052     },
11053
11054         /* @private */
11055     afterFx : function(o){
11056         if(o.afterStyle){
11057             this.applyStyles(o.afterStyle);
11058         }
11059         if(o.afterCls){
11060             this.addClass(o.afterCls);
11061         }
11062         if(o.remove === true){
11063             this.remove();
11064         }
11065         Roo.callback(o.callback, o.scope, [this]);
11066         if(!o.concurrent){
11067             this.fxQueue.shift();
11068             this.nextFx();
11069         }
11070     },
11071
11072         /* @private */
11073     getFxEl : function(){ // support for composite element fx
11074         return Roo.get(this.dom);
11075     },
11076
11077         /* @private */
11078     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11079         animType = animType || 'run';
11080         opt = opt || {};
11081         var anim = Roo.lib.Anim[animType](
11082             this.dom, args,
11083             (opt.duration || defaultDur) || .35,
11084             (opt.easing || defaultEase) || 'easeOut',
11085             function(){
11086                 Roo.callback(cb, this);
11087             },
11088             this
11089         );
11090         opt.anim = anim;
11091         return anim;
11092     }
11093 };
11094
11095 // backwords compat
11096 Roo.Fx.resize = Roo.Fx.scale;
11097
11098 //When included, Roo.Fx is automatically applied to Element so that all basic
11099 //effects are available directly via the Element API
11100 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11101  * Based on:
11102  * Ext JS Library 1.1.1
11103  * Copyright(c) 2006-2007, Ext JS, LLC.
11104  *
11105  * Originally Released Under LGPL - original licence link has changed is not relivant.
11106  *
11107  * Fork - LGPL
11108  * <script type="text/javascript">
11109  */
11110
11111
11112 /**
11113  * @class Roo.CompositeElement
11114  * Standard composite class. Creates a Roo.Element for every element in the collection.
11115  * <br><br>
11116  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11117  * actions will be performed on all the elements in this collection.</b>
11118  * <br><br>
11119  * All methods return <i>this</i> and can be chained.
11120  <pre><code>
11121  var els = Roo.select("#some-el div.some-class", true);
11122  // or select directly from an existing element
11123  var el = Roo.get('some-el');
11124  el.select('div.some-class', true);
11125
11126  els.setWidth(100); // all elements become 100 width
11127  els.hide(true); // all elements fade out and hide
11128  // or
11129  els.setWidth(100).hide(true);
11130  </code></pre>
11131  */
11132 Roo.CompositeElement = function(els){
11133     this.elements = [];
11134     this.addElements(els);
11135 };
11136 Roo.CompositeElement.prototype = {
11137     isComposite: true,
11138     addElements : function(els){
11139         if(!els) {
11140             return this;
11141         }
11142         if(typeof els == "string"){
11143             els = Roo.Element.selectorFunction(els);
11144         }
11145         var yels = this.elements;
11146         var index = yels.length-1;
11147         for(var i = 0, len = els.length; i < len; i++) {
11148                 yels[++index] = Roo.get(els[i]);
11149         }
11150         return this;
11151     },
11152
11153     /**
11154     * Clears this composite and adds the elements returned by the passed selector.
11155     * @param {String/Array} els A string CSS selector, an array of elements or an element
11156     * @return {CompositeElement} this
11157     */
11158     fill : function(els){
11159         this.elements = [];
11160         this.add(els);
11161         return this;
11162     },
11163
11164     /**
11165     * Filters this composite to only elements that match the passed selector.
11166     * @param {String} selector A string CSS selector
11167     * @param {Boolean} inverse return inverse filter (not matches)
11168     * @return {CompositeElement} this
11169     */
11170     filter : function(selector, inverse){
11171         var els = [];
11172         inverse = inverse || false;
11173         this.each(function(el){
11174             var match = inverse ? !el.is(selector) : el.is(selector);
11175             if(match){
11176                 els[els.length] = el.dom;
11177             }
11178         });
11179         this.fill(els);
11180         return this;
11181     },
11182
11183     invoke : function(fn, args){
11184         var els = this.elements;
11185         for(var i = 0, len = els.length; i < len; i++) {
11186                 Roo.Element.prototype[fn].apply(els[i], args);
11187         }
11188         return this;
11189     },
11190     /**
11191     * Adds elements to this composite.
11192     * @param {String/Array} els A string CSS selector, an array of elements or an element
11193     * @return {CompositeElement} this
11194     */
11195     add : function(els){
11196         if(typeof els == "string"){
11197             this.addElements(Roo.Element.selectorFunction(els));
11198         }else if(els.length !== undefined){
11199             this.addElements(els);
11200         }else{
11201             this.addElements([els]);
11202         }
11203         return this;
11204     },
11205     /**
11206     * Calls the passed function passing (el, this, index) for each element in this composite.
11207     * @param {Function} fn The function to call
11208     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11209     * @return {CompositeElement} this
11210     */
11211     each : function(fn, scope){
11212         var els = this.elements;
11213         for(var i = 0, len = els.length; i < len; i++){
11214             if(fn.call(scope || els[i], els[i], this, i) === false) {
11215                 break;
11216             }
11217         }
11218         return this;
11219     },
11220
11221     /**
11222      * Returns the Element object at the specified index
11223      * @param {Number} index
11224      * @return {Roo.Element}
11225      */
11226     item : function(index){
11227         return this.elements[index] || null;
11228     },
11229
11230     /**
11231      * Returns the first Element
11232      * @return {Roo.Element}
11233      */
11234     first : function(){
11235         return this.item(0);
11236     },
11237
11238     /**
11239      * Returns the last Element
11240      * @return {Roo.Element}
11241      */
11242     last : function(){
11243         return this.item(this.elements.length-1);
11244     },
11245
11246     /**
11247      * Returns the number of elements in this composite
11248      * @return Number
11249      */
11250     getCount : function(){
11251         return this.elements.length;
11252     },
11253
11254     /**
11255      * Returns true if this composite contains the passed element
11256      * @return Boolean
11257      */
11258     contains : function(el){
11259         return this.indexOf(el) !== -1;
11260     },
11261
11262     /**
11263      * Returns true if this composite contains the passed element
11264      * @return Boolean
11265      */
11266     indexOf : function(el){
11267         return this.elements.indexOf(Roo.get(el));
11268     },
11269
11270
11271     /**
11272     * Removes the specified element(s).
11273     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11274     * or an array of any of those.
11275     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11276     * @return {CompositeElement} this
11277     */
11278     removeElement : function(el, removeDom){
11279         if(el instanceof Array){
11280             for(var i = 0, len = el.length; i < len; i++){
11281                 this.removeElement(el[i]);
11282             }
11283             return this;
11284         }
11285         var index = typeof el == 'number' ? el : this.indexOf(el);
11286         if(index !== -1){
11287             if(removeDom){
11288                 var d = this.elements[index];
11289                 if(d.dom){
11290                     d.remove();
11291                 }else{
11292                     d.parentNode.removeChild(d);
11293                 }
11294             }
11295             this.elements.splice(index, 1);
11296         }
11297         return this;
11298     },
11299
11300     /**
11301     * Replaces the specified element with the passed element.
11302     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11303     * to replace.
11304     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11305     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11306     * @return {CompositeElement} this
11307     */
11308     replaceElement : function(el, replacement, domReplace){
11309         var index = typeof el == 'number' ? el : this.indexOf(el);
11310         if(index !== -1){
11311             if(domReplace){
11312                 this.elements[index].replaceWith(replacement);
11313             }else{
11314                 this.elements.splice(index, 1, Roo.get(replacement))
11315             }
11316         }
11317         return this;
11318     },
11319
11320     /**
11321      * Removes all elements.
11322      */
11323     clear : function(){
11324         this.elements = [];
11325     }
11326 };
11327 (function(){
11328     Roo.CompositeElement.createCall = function(proto, fnName){
11329         if(!proto[fnName]){
11330             proto[fnName] = function(){
11331                 return this.invoke(fnName, arguments);
11332             };
11333         }
11334     };
11335     for(var fnName in Roo.Element.prototype){
11336         if(typeof Roo.Element.prototype[fnName] == "function"){
11337             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11338         }
11339     };
11340 })();
11341 /*
11342  * Based on:
11343  * Ext JS Library 1.1.1
11344  * Copyright(c) 2006-2007, Ext JS, LLC.
11345  *
11346  * Originally Released Under LGPL - original licence link has changed is not relivant.
11347  *
11348  * Fork - LGPL
11349  * <script type="text/javascript">
11350  */
11351
11352 /**
11353  * @class Roo.CompositeElementLite
11354  * @extends Roo.CompositeElement
11355  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11356  <pre><code>
11357  var els = Roo.select("#some-el div.some-class");
11358  // or select directly from an existing element
11359  var el = Roo.get('some-el');
11360  el.select('div.some-class');
11361
11362  els.setWidth(100); // all elements become 100 width
11363  els.hide(true); // all elements fade out and hide
11364  // or
11365  els.setWidth(100).hide(true);
11366  </code></pre><br><br>
11367  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11368  * actions will be performed on all the elements in this collection.</b>
11369  */
11370 Roo.CompositeElementLite = function(els){
11371     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11372     this.el = new Roo.Element.Flyweight();
11373 };
11374 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11375     addElements : function(els){
11376         if(els){
11377             if(els instanceof Array){
11378                 this.elements = this.elements.concat(els);
11379             }else{
11380                 var yels = this.elements;
11381                 var index = yels.length-1;
11382                 for(var i = 0, len = els.length; i < len; i++) {
11383                     yels[++index] = els[i];
11384                 }
11385             }
11386         }
11387         return this;
11388     },
11389     invoke : function(fn, args){
11390         var els = this.elements;
11391         var el = this.el;
11392         for(var i = 0, len = els.length; i < len; i++) {
11393             el.dom = els[i];
11394                 Roo.Element.prototype[fn].apply(el, args);
11395         }
11396         return this;
11397     },
11398     /**
11399      * Returns a flyweight Element of the dom element object at the specified index
11400      * @param {Number} index
11401      * @return {Roo.Element}
11402      */
11403     item : function(index){
11404         if(!this.elements[index]){
11405             return null;
11406         }
11407         this.el.dom = this.elements[index];
11408         return this.el;
11409     },
11410
11411     // fixes scope with flyweight
11412     addListener : function(eventName, handler, scope, opt){
11413         var els = this.elements;
11414         for(var i = 0, len = els.length; i < len; i++) {
11415             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11416         }
11417         return this;
11418     },
11419
11420     /**
11421     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11422     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11423     * a reference to the dom node, use el.dom.</b>
11424     * @param {Function} fn The function to call
11425     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11426     * @return {CompositeElement} this
11427     */
11428     each : function(fn, scope){
11429         var els = this.elements;
11430         var el = this.el;
11431         for(var i = 0, len = els.length; i < len; i++){
11432             el.dom = els[i];
11433                 if(fn.call(scope || el, el, this, i) === false){
11434                 break;
11435             }
11436         }
11437         return this;
11438     },
11439
11440     indexOf : function(el){
11441         return this.elements.indexOf(Roo.getDom(el));
11442     },
11443
11444     replaceElement : function(el, replacement, domReplace){
11445         var index = typeof el == 'number' ? el : this.indexOf(el);
11446         if(index !== -1){
11447             replacement = Roo.getDom(replacement);
11448             if(domReplace){
11449                 var d = this.elements[index];
11450                 d.parentNode.insertBefore(replacement, d);
11451                 d.parentNode.removeChild(d);
11452             }
11453             this.elements.splice(index, 1, replacement);
11454         }
11455         return this;
11456     }
11457 });
11458 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11459
11460 /*
11461  * Based on:
11462  * Ext JS Library 1.1.1
11463  * Copyright(c) 2006-2007, Ext JS, LLC.
11464  *
11465  * Originally Released Under LGPL - original licence link has changed is not relivant.
11466  *
11467  * Fork - LGPL
11468  * <script type="text/javascript">
11469  */
11470
11471  
11472
11473 /**
11474  * @class Roo.data.Connection
11475  * @extends Roo.util.Observable
11476  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11477  * either to a configured URL, or to a URL specified at request time. 
11478  * 
11479  * Requests made by this class are asynchronous, and will return immediately. No data from
11480  * the server will be available to the statement immediately following the {@link #request} call.
11481  * To process returned data, use a callback in the request options object, or an event listener.
11482  * 
11483  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11484  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11485  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11486  * property and, if present, the IFRAME's XML document as the responseXML property.
11487  * 
11488  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11489  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11490  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11491  * standard DOM methods.
11492  * @constructor
11493  * @param {Object} config a configuration object.
11494  */
11495 Roo.data.Connection = function(config){
11496     Roo.apply(this, config);
11497     this.addEvents({
11498         /**
11499          * @event beforerequest
11500          * Fires before a network request is made to retrieve a data object.
11501          * @param {Connection} conn This Connection object.
11502          * @param {Object} options The options config object passed to the {@link #request} method.
11503          */
11504         "beforerequest" : true,
11505         /**
11506          * @event requestcomplete
11507          * Fires if the request was successfully completed.
11508          * @param {Connection} conn This Connection object.
11509          * @param {Object} response The XHR object containing the response data.
11510          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11511          * @param {Object} options The options config object passed to the {@link #request} method.
11512          */
11513         "requestcomplete" : true,
11514         /**
11515          * @event requestexception
11516          * Fires if an error HTTP status was returned from the server.
11517          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11518          * @param {Connection} conn This Connection object.
11519          * @param {Object} response The XHR object containing the response data.
11520          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11521          * @param {Object} options The options config object passed to the {@link #request} method.
11522          */
11523         "requestexception" : true
11524     });
11525     Roo.data.Connection.superclass.constructor.call(this);
11526 };
11527
11528 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11529     /**
11530      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11531      */
11532     /**
11533      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11534      * extra parameters to each request made by this object. (defaults to undefined)
11535      */
11536     /**
11537      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11538      *  to each request made by this object. (defaults to undefined)
11539      */
11540     /**
11541      * @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)
11542      */
11543     /**
11544      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11545      */
11546     timeout : 30000,
11547     /**
11548      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11549      * @type Boolean
11550      */
11551     autoAbort:false,
11552
11553     /**
11554      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11555      * @type Boolean
11556      */
11557     disableCaching: true,
11558
11559     /**
11560      * Sends an HTTP request to a remote server.
11561      * @param {Object} options An object which may contain the following properties:<ul>
11562      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11563      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11564      * request, a url encoded string or a function to call to get either.</li>
11565      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11566      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11567      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11568      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11569      * <li>options {Object} The parameter to the request call.</li>
11570      * <li>success {Boolean} True if the request succeeded.</li>
11571      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11572      * </ul></li>
11573      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11574      * The callback is passed the following parameters:<ul>
11575      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11576      * <li>options {Object} The parameter to the request call.</li>
11577      * </ul></li>
11578      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11579      * The callback is passed the following parameters:<ul>
11580      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11581      * <li>options {Object} The parameter to the request call.</li>
11582      * </ul></li>
11583      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11584      * for the callback function. Defaults to the browser window.</li>
11585      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11586      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11587      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11588      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11589      * params for the post data. Any params will be appended to the URL.</li>
11590      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11591      * </ul>
11592      * @return {Number} transactionId
11593      */
11594     request : function(o){
11595         if(this.fireEvent("beforerequest", this, o) !== false){
11596             var p = o.params;
11597
11598             if(typeof p == "function"){
11599                 p = p.call(o.scope||window, o);
11600             }
11601             if(typeof p == "object"){
11602                 p = Roo.urlEncode(o.params);
11603             }
11604             if(this.extraParams){
11605                 var extras = Roo.urlEncode(this.extraParams);
11606                 p = p ? (p + '&' + extras) : extras;
11607             }
11608
11609             var url = o.url || this.url;
11610             if(typeof url == 'function'){
11611                 url = url.call(o.scope||window, o);
11612             }
11613
11614             if(o.form){
11615                 var form = Roo.getDom(o.form);
11616                 url = url || form.action;
11617
11618                 var enctype = form.getAttribute("enctype");
11619                 
11620                 if (o.formData) {
11621                     return this.doFormDataUpload(o,p,url);
11622                 }
11623                 
11624                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11625                     return this.doFormUpload(o, p, url);
11626                 }
11627                 var f = Roo.lib.Ajax.serializeForm(form);
11628                 p = p ? (p + '&' + f) : f;
11629             }
11630
11631             var hs = o.headers;
11632             if(this.defaultHeaders){
11633                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11634                 if(!o.headers){
11635                     o.headers = hs;
11636                 }
11637             }
11638
11639             var cb = {
11640                 success: this.handleResponse,
11641                 failure: this.handleFailure,
11642                 scope: this,
11643                 argument: {options: o},
11644                 timeout : o.timeout || this.timeout
11645             };
11646
11647             var method = o.method||this.method||(p ? "POST" : "GET");
11648
11649             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11650                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11651             }
11652
11653             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11654                 if(o.autoAbort){
11655                     this.abort();
11656                 }
11657             }else if(this.autoAbort !== false){
11658                 this.abort();
11659             }
11660
11661             if((method == 'GET' && p) || o.xmlData){
11662                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11663                 p = '';
11664             }
11665             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11666             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11667             Roo.lib.Ajax.useDefaultHeader == true;
11668             return this.transId;
11669         }else{
11670             Roo.callback(o.callback, o.scope, [o, null, null]);
11671             return null;
11672         }
11673     },
11674
11675     /**
11676      * Determine whether this object has a request outstanding.
11677      * @param {Number} transactionId (Optional) defaults to the last transaction
11678      * @return {Boolean} True if there is an outstanding request.
11679      */
11680     isLoading : function(transId){
11681         if(transId){
11682             return Roo.lib.Ajax.isCallInProgress(transId);
11683         }else{
11684             return this.transId ? true : false;
11685         }
11686     },
11687
11688     /**
11689      * Aborts any outstanding request.
11690      * @param {Number} transactionId (Optional) defaults to the last transaction
11691      */
11692     abort : function(transId){
11693         if(transId || this.isLoading()){
11694             Roo.lib.Ajax.abort(transId || this.transId);
11695         }
11696     },
11697
11698     // private
11699     handleResponse : function(response){
11700         this.transId = false;
11701         var options = response.argument.options;
11702         response.argument = options ? options.argument : null;
11703         this.fireEvent("requestcomplete", this, response, options);
11704         Roo.callback(options.success, options.scope, [response, options]);
11705         Roo.callback(options.callback, options.scope, [options, true, response]);
11706     },
11707
11708     // private
11709     handleFailure : function(response, e){
11710         this.transId = false;
11711         var options = response.argument.options;
11712         response.argument = options ? options.argument : null;
11713         this.fireEvent("requestexception", this, response, options, e);
11714         Roo.callback(options.failure, options.scope, [response, options]);
11715         Roo.callback(options.callback, options.scope, [options, false, response]);
11716     },
11717
11718     // private
11719     doFormUpload : function(o, ps, url){
11720         var id = Roo.id();
11721         var frame = document.createElement('iframe');
11722         frame.id = id;
11723         frame.name = id;
11724         frame.className = 'x-hidden';
11725         if(Roo.isIE){
11726             frame.src = Roo.SSL_SECURE_URL;
11727         }
11728         document.body.appendChild(frame);
11729
11730         if(Roo.isIE){
11731            document.frames[id].name = id;
11732         }
11733
11734         var form = Roo.getDom(o.form);
11735         form.target = id;
11736         form.method = 'POST';
11737         form.enctype = form.encoding = 'multipart/form-data';
11738         if(url){
11739             form.action = url;
11740         }
11741
11742         var hiddens, hd;
11743         if(ps){ // add dynamic params
11744             hiddens = [];
11745             ps = Roo.urlDecode(ps, false);
11746             for(var k in ps){
11747                 if(ps.hasOwnProperty(k)){
11748                     hd = document.createElement('input');
11749                     hd.type = 'hidden';
11750                     hd.name = k;
11751                     hd.value = ps[k];
11752                     form.appendChild(hd);
11753                     hiddens.push(hd);
11754                 }
11755             }
11756         }
11757
11758         function cb(){
11759             var r = {  // bogus response object
11760                 responseText : '',
11761                 responseXML : null
11762             };
11763
11764             r.argument = o ? o.argument : null;
11765
11766             try { //
11767                 var doc;
11768                 if(Roo.isIE){
11769                     doc = frame.contentWindow.document;
11770                 }else {
11771                     doc = (frame.contentDocument || window.frames[id].document);
11772                 }
11773                 if(doc && doc.body){
11774                     r.responseText = doc.body.innerHTML;
11775                 }
11776                 if(doc && doc.XMLDocument){
11777                     r.responseXML = doc.XMLDocument;
11778                 }else {
11779                     r.responseXML = doc;
11780                 }
11781             }
11782             catch(e) {
11783                 // ignore
11784             }
11785
11786             Roo.EventManager.removeListener(frame, 'load', cb, this);
11787
11788             this.fireEvent("requestcomplete", this, r, o);
11789             Roo.callback(o.success, o.scope, [r, o]);
11790             Roo.callback(o.callback, o.scope, [o, true, r]);
11791
11792             setTimeout(function(){document.body.removeChild(frame);}, 100);
11793         }
11794
11795         Roo.EventManager.on(frame, 'load', cb, this);
11796         form.submit();
11797
11798         if(hiddens){ // remove dynamic params
11799             for(var i = 0, len = hiddens.length; i < len; i++){
11800                 form.removeChild(hiddens[i]);
11801             }
11802         }
11803     },
11804     // this is a 'formdata version???'
11805     
11806     
11807     doFormDataUpload : function(o, ps, url)
11808     {
11809         var form = Roo.getDom(o.form);
11810         form.enctype = form.encoding = 'multipart/form-data';
11811         var formData = o.formData === true ? new FormData(form) : o.formData;
11812       
11813         var cb = {
11814             success: this.handleResponse,
11815             failure: this.handleFailure,
11816             scope: this,
11817             argument: {options: o},
11818             timeout : o.timeout || this.timeout
11819         };
11820  
11821         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11822             if(o.autoAbort){
11823                 this.abort();
11824             }
11825         }else if(this.autoAbort !== false){
11826             this.abort();
11827         }
11828
11829         //Roo.lib.Ajax.defaultPostHeader = null;
11830         Roo.lib.Ajax.useDefaultHeader = false;
11831         this.transId = Roo.lib.Ajax.request( "POST", url, cb, o.formData, o);
11832         Roo.lib.Ajax.useDefaultHeader = true;
11833  
11834          
11835     }
11836     
11837 });
11838 /*
11839  * Based on:
11840  * Ext JS Library 1.1.1
11841  * Copyright(c) 2006-2007, Ext JS, LLC.
11842  *
11843  * Originally Released Under LGPL - original licence link has changed is not relivant.
11844  *
11845  * Fork - LGPL
11846  * <script type="text/javascript">
11847  */
11848  
11849 /**
11850  * Global Ajax request class.
11851  * 
11852  * @class Roo.Ajax
11853  * @extends Roo.data.Connection
11854  * @static
11855  * 
11856  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11857  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11858  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11859  * @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)
11860  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11861  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11862  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11863  */
11864 Roo.Ajax = new Roo.data.Connection({
11865     // fix up the docs
11866     /**
11867      * @scope Roo.Ajax
11868      * @type {Boolear} 
11869      */
11870     autoAbort : false,
11871
11872     /**
11873      * Serialize the passed form into a url encoded string
11874      * @scope Roo.Ajax
11875      * @param {String/HTMLElement} form
11876      * @return {String}
11877      */
11878     serializeForm : function(form){
11879         return Roo.lib.Ajax.serializeForm(form);
11880     }
11881 });/*
11882  * Based on:
11883  * Ext JS Library 1.1.1
11884  * Copyright(c) 2006-2007, Ext JS, LLC.
11885  *
11886  * Originally Released Under LGPL - original licence link has changed is not relivant.
11887  *
11888  * Fork - LGPL
11889  * <script type="text/javascript">
11890  */
11891
11892  
11893 /**
11894  * @class Roo.UpdateManager
11895  * @extends Roo.util.Observable
11896  * Provides AJAX-style update for Element object.<br><br>
11897  * Usage:<br>
11898  * <pre><code>
11899  * // Get it from a Roo.Element object
11900  * var el = Roo.get("foo");
11901  * var mgr = el.getUpdateManager();
11902  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11903  * ...
11904  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11905  * <br>
11906  * // or directly (returns the same UpdateManager instance)
11907  * var mgr = new Roo.UpdateManager("myElementId");
11908  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11909  * mgr.on("update", myFcnNeedsToKnow);
11910  * <br>
11911    // short handed call directly from the element object
11912    Roo.get("foo").load({
11913         url: "bar.php",
11914         scripts:true,
11915         params: "for=bar",
11916         text: "Loading Foo..."
11917    });
11918  * </code></pre>
11919  * @constructor
11920  * Create new UpdateManager directly.
11921  * @param {String/HTMLElement/Roo.Element} el The element to update
11922  * @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).
11923  */
11924 Roo.UpdateManager = function(el, forceNew){
11925     el = Roo.get(el);
11926     if(!forceNew && el.updateManager){
11927         return el.updateManager;
11928     }
11929     /**
11930      * The Element object
11931      * @type Roo.Element
11932      */
11933     this.el = el;
11934     /**
11935      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11936      * @type String
11937      */
11938     this.defaultUrl = null;
11939
11940     this.addEvents({
11941         /**
11942          * @event beforeupdate
11943          * Fired before an update is made, return false from your handler and the update is cancelled.
11944          * @param {Roo.Element} el
11945          * @param {String/Object/Function} url
11946          * @param {String/Object} params
11947          */
11948         "beforeupdate": true,
11949         /**
11950          * @event update
11951          * Fired after successful update is made.
11952          * @param {Roo.Element} el
11953          * @param {Object} oResponseObject The response Object
11954          */
11955         "update": true,
11956         /**
11957          * @event failure
11958          * Fired on update failure.
11959          * @param {Roo.Element} el
11960          * @param {Object} oResponseObject The response Object
11961          */
11962         "failure": true
11963     });
11964     var d = Roo.UpdateManager.defaults;
11965     /**
11966      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11967      * @type String
11968      */
11969     this.sslBlankUrl = d.sslBlankUrl;
11970     /**
11971      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11972      * @type Boolean
11973      */
11974     this.disableCaching = d.disableCaching;
11975     /**
11976      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11977      * @type String
11978      */
11979     this.indicatorText = d.indicatorText;
11980     /**
11981      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11982      * @type String
11983      */
11984     this.showLoadIndicator = d.showLoadIndicator;
11985     /**
11986      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11987      * @type Number
11988      */
11989     this.timeout = d.timeout;
11990
11991     /**
11992      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11993      * @type Boolean
11994      */
11995     this.loadScripts = d.loadScripts;
11996
11997     /**
11998      * Transaction object of current executing transaction
11999      */
12000     this.transaction = null;
12001
12002     /**
12003      * @private
12004      */
12005     this.autoRefreshProcId = null;
12006     /**
12007      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12008      * @type Function
12009      */
12010     this.refreshDelegate = this.refresh.createDelegate(this);
12011     /**
12012      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12013      * @type Function
12014      */
12015     this.updateDelegate = this.update.createDelegate(this);
12016     /**
12017      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12018      * @type Function
12019      */
12020     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12021     /**
12022      * @private
12023      */
12024     this.successDelegate = this.processSuccess.createDelegate(this);
12025     /**
12026      * @private
12027      */
12028     this.failureDelegate = this.processFailure.createDelegate(this);
12029
12030     if(!this.renderer){
12031      /**
12032       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12033       */
12034     this.renderer = new Roo.UpdateManager.BasicRenderer();
12035     }
12036     
12037     Roo.UpdateManager.superclass.constructor.call(this);
12038 };
12039
12040 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12041     /**
12042      * Get the Element this UpdateManager is bound to
12043      * @return {Roo.Element} The element
12044      */
12045     getEl : function(){
12046         return this.el;
12047     },
12048     /**
12049      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12050      * @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:
12051 <pre><code>
12052 um.update({<br/>
12053     url: "your-url.php",<br/>
12054     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12055     callback: yourFunction,<br/>
12056     scope: yourObject, //(optional scope)  <br/>
12057     discardUrl: false, <br/>
12058     nocache: false,<br/>
12059     text: "Loading...",<br/>
12060     timeout: 30,<br/>
12061     scripts: false<br/>
12062 });
12063 </code></pre>
12064      * The only required property is url. The optional properties nocache, text and scripts
12065      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12066      * @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}
12067      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12068      * @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.
12069      */
12070     update : function(url, params, callback, discardUrl){
12071         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12072             var method = this.method,
12073                 cfg;
12074             if(typeof url == "object"){ // must be config object
12075                 cfg = url;
12076                 url = cfg.url;
12077                 params = params || cfg.params;
12078                 callback = callback || cfg.callback;
12079                 discardUrl = discardUrl || cfg.discardUrl;
12080                 if(callback && cfg.scope){
12081                     callback = callback.createDelegate(cfg.scope);
12082                 }
12083                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12084                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12085                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12086                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12087                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12088             }
12089             this.showLoading();
12090             if(!discardUrl){
12091                 this.defaultUrl = url;
12092             }
12093             if(typeof url == "function"){
12094                 url = url.call(this);
12095             }
12096
12097             method = method || (params ? "POST" : "GET");
12098             if(method == "GET"){
12099                 url = this.prepareUrl(url);
12100             }
12101
12102             var o = Roo.apply(cfg ||{}, {
12103                 url : url,
12104                 params: params,
12105                 success: this.successDelegate,
12106                 failure: this.failureDelegate,
12107                 callback: undefined,
12108                 timeout: (this.timeout*1000),
12109                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12110             });
12111             Roo.log("updated manager called with timeout of " + o.timeout);
12112             this.transaction = Roo.Ajax.request(o);
12113         }
12114     },
12115
12116     /**
12117      * 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.
12118      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12119      * @param {String/HTMLElement} form The form Id or form element
12120      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12121      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12122      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12123      */
12124     formUpdate : function(form, url, reset, callback){
12125         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12126             if(typeof url == "function"){
12127                 url = url.call(this);
12128             }
12129             form = Roo.getDom(form);
12130             this.transaction = Roo.Ajax.request({
12131                 form: form,
12132                 url:url,
12133                 success: this.successDelegate,
12134                 failure: this.failureDelegate,
12135                 timeout: (this.timeout*1000),
12136                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12137             });
12138             this.showLoading.defer(1, this);
12139         }
12140     },
12141
12142     /**
12143      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12144      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12145      */
12146     refresh : function(callback){
12147         if(this.defaultUrl == null){
12148             return;
12149         }
12150         this.update(this.defaultUrl, null, callback, true);
12151     },
12152
12153     /**
12154      * Set this element to auto refresh.
12155      * @param {Number} interval How often to update (in seconds).
12156      * @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)
12157      * @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}
12158      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12159      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12160      */
12161     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12162         if(refreshNow){
12163             this.update(url || this.defaultUrl, params, callback, true);
12164         }
12165         if(this.autoRefreshProcId){
12166             clearInterval(this.autoRefreshProcId);
12167         }
12168         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12169     },
12170
12171     /**
12172      * Stop auto refresh on this element.
12173      */
12174      stopAutoRefresh : function(){
12175         if(this.autoRefreshProcId){
12176             clearInterval(this.autoRefreshProcId);
12177             delete this.autoRefreshProcId;
12178         }
12179     },
12180
12181     isAutoRefreshing : function(){
12182        return this.autoRefreshProcId ? true : false;
12183     },
12184     /**
12185      * Called to update the element to "Loading" state. Override to perform custom action.
12186      */
12187     showLoading : function(){
12188         if(this.showLoadIndicator){
12189             this.el.update(this.indicatorText);
12190         }
12191     },
12192
12193     /**
12194      * Adds unique parameter to query string if disableCaching = true
12195      * @private
12196      */
12197     prepareUrl : function(url){
12198         if(this.disableCaching){
12199             var append = "_dc=" + (new Date().getTime());
12200             if(url.indexOf("?") !== -1){
12201                 url += "&" + append;
12202             }else{
12203                 url += "?" + append;
12204             }
12205         }
12206         return url;
12207     },
12208
12209     /**
12210      * @private
12211      */
12212     processSuccess : function(response){
12213         this.transaction = null;
12214         if(response.argument.form && response.argument.reset){
12215             try{ // put in try/catch since some older FF releases had problems with this
12216                 response.argument.form.reset();
12217             }catch(e){}
12218         }
12219         if(this.loadScripts){
12220             this.renderer.render(this.el, response, this,
12221                 this.updateComplete.createDelegate(this, [response]));
12222         }else{
12223             this.renderer.render(this.el, response, this);
12224             this.updateComplete(response);
12225         }
12226     },
12227
12228     updateComplete : function(response){
12229         this.fireEvent("update", this.el, response);
12230         if(typeof response.argument.callback == "function"){
12231             response.argument.callback(this.el, true, response);
12232         }
12233     },
12234
12235     /**
12236      * @private
12237      */
12238     processFailure : function(response){
12239         this.transaction = null;
12240         this.fireEvent("failure", this.el, response);
12241         if(typeof response.argument.callback == "function"){
12242             response.argument.callback(this.el, false, response);
12243         }
12244     },
12245
12246     /**
12247      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12248      * @param {Object} renderer The object implementing the render() method
12249      */
12250     setRenderer : function(renderer){
12251         this.renderer = renderer;
12252     },
12253
12254     getRenderer : function(){
12255        return this.renderer;
12256     },
12257
12258     /**
12259      * Set the defaultUrl used for updates
12260      * @param {String/Function} defaultUrl The url or a function to call to get the url
12261      */
12262     setDefaultUrl : function(defaultUrl){
12263         this.defaultUrl = defaultUrl;
12264     },
12265
12266     /**
12267      * Aborts the executing transaction
12268      */
12269     abort : function(){
12270         if(this.transaction){
12271             Roo.Ajax.abort(this.transaction);
12272         }
12273     },
12274
12275     /**
12276      * Returns true if an update is in progress
12277      * @return {Boolean}
12278      */
12279     isUpdating : function(){
12280         if(this.transaction){
12281             return Roo.Ajax.isLoading(this.transaction);
12282         }
12283         return false;
12284     }
12285 });
12286
12287 /**
12288  * @class Roo.UpdateManager.defaults
12289  * @static (not really - but it helps the doc tool)
12290  * The defaults collection enables customizing the default properties of UpdateManager
12291  */
12292    Roo.UpdateManager.defaults = {
12293        /**
12294          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12295          * @type Number
12296          */
12297          timeout : 30,
12298
12299          /**
12300          * True to process scripts by default (Defaults to false).
12301          * @type Boolean
12302          */
12303         loadScripts : false,
12304
12305         /**
12306         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12307         * @type String
12308         */
12309         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12310         /**
12311          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12312          * @type Boolean
12313          */
12314         disableCaching : false,
12315         /**
12316          * Whether to show indicatorText when loading (Defaults to true).
12317          * @type Boolean
12318          */
12319         showLoadIndicator : true,
12320         /**
12321          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12322          * @type String
12323          */
12324         indicatorText : '<div class="loading-indicator">Loading...</div>'
12325    };
12326
12327 /**
12328  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12329  *Usage:
12330  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12331  * @param {String/HTMLElement/Roo.Element} el The element to update
12332  * @param {String} url The url
12333  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12334  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12335  * @static
12336  * @deprecated
12337  * @member Roo.UpdateManager
12338  */
12339 Roo.UpdateManager.updateElement = function(el, url, params, options){
12340     var um = Roo.get(el, true).getUpdateManager();
12341     Roo.apply(um, options);
12342     um.update(url, params, options ? options.callback : null);
12343 };
12344 // alias for backwards compat
12345 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12346 /**
12347  * @class Roo.UpdateManager.BasicRenderer
12348  * Default Content renderer. Updates the elements innerHTML with the responseText.
12349  */
12350 Roo.UpdateManager.BasicRenderer = function(){};
12351
12352 Roo.UpdateManager.BasicRenderer.prototype = {
12353     /**
12354      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12355      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12356      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12357      * @param {Roo.Element} el The element being rendered
12358      * @param {Object} response The YUI Connect response object
12359      * @param {UpdateManager} updateManager The calling update manager
12360      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12361      */
12362      render : function(el, response, updateManager, callback){
12363         el.update(response.responseText, updateManager.loadScripts, callback);
12364     }
12365 };
12366 /*
12367  * Based on:
12368  * Roo JS
12369  * (c)) Alan Knowles
12370  * Licence : LGPL
12371  */
12372
12373
12374 /**
12375  * @class Roo.DomTemplate
12376  * @extends Roo.Template
12377  * An effort at a dom based template engine..
12378  *
12379  * Similar to XTemplate, except it uses dom parsing to create the template..
12380  *
12381  * Supported features:
12382  *
12383  *  Tags:
12384
12385 <pre><code>
12386       {a_variable} - output encoded.
12387       {a_variable.format:("Y-m-d")} - call a method on the variable
12388       {a_variable:raw} - unencoded output
12389       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12390       {a_variable:this.method_on_template(...)} - call a method on the template object.
12391  
12392 </code></pre>
12393  *  The tpl tag:
12394 <pre><code>
12395         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12396         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12397         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12398         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12399   
12400 </code></pre>
12401  *      
12402  */
12403 Roo.DomTemplate = function()
12404 {
12405      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12406      if (this.html) {
12407         this.compile();
12408      }
12409 };
12410
12411
12412 Roo.extend(Roo.DomTemplate, Roo.Template, {
12413     /**
12414      * id counter for sub templates.
12415      */
12416     id : 0,
12417     /**
12418      * flag to indicate if dom parser is inside a pre,
12419      * it will strip whitespace if not.
12420      */
12421     inPre : false,
12422     
12423     /**
12424      * The various sub templates
12425      */
12426     tpls : false,
12427     
12428     
12429     
12430     /**
12431      *
12432      * basic tag replacing syntax
12433      * WORD:WORD()
12434      *
12435      * // you can fake an object call by doing this
12436      *  x.t:(test,tesT) 
12437      * 
12438      */
12439     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12440     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12441     
12442     iterChild : function (node, method) {
12443         
12444         var oldPre = this.inPre;
12445         if (node.tagName == 'PRE') {
12446             this.inPre = true;
12447         }
12448         for( var i = 0; i < node.childNodes.length; i++) {
12449             method.call(this, node.childNodes[i]);
12450         }
12451         this.inPre = oldPre;
12452     },
12453     
12454     
12455     
12456     /**
12457      * compile the template
12458      *
12459      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12460      *
12461      */
12462     compile: function()
12463     {
12464         var s = this.html;
12465         
12466         // covert the html into DOM...
12467         var doc = false;
12468         var div =false;
12469         try {
12470             doc = document.implementation.createHTMLDocument("");
12471             doc.documentElement.innerHTML =   this.html  ;
12472             div = doc.documentElement;
12473         } catch (e) {
12474             // old IE... - nasty -- it causes all sorts of issues.. with
12475             // images getting pulled from server..
12476             div = document.createElement('div');
12477             div.innerHTML = this.html;
12478         }
12479         //doc.documentElement.innerHTML = htmlBody
12480          
12481         
12482         
12483         this.tpls = [];
12484         var _t = this;
12485         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12486         
12487         var tpls = this.tpls;
12488         
12489         // create a top level template from the snippet..
12490         
12491         //Roo.log(div.innerHTML);
12492         
12493         var tpl = {
12494             uid : 'master',
12495             id : this.id++,
12496             attr : false,
12497             value : false,
12498             body : div.innerHTML,
12499             
12500             forCall : false,
12501             execCall : false,
12502             dom : div,
12503             isTop : true
12504             
12505         };
12506         tpls.unshift(tpl);
12507         
12508         
12509         // compile them...
12510         this.tpls = [];
12511         Roo.each(tpls, function(tp){
12512             this.compileTpl(tp);
12513             this.tpls[tp.id] = tp;
12514         }, this);
12515         
12516         this.master = tpls[0];
12517         return this;
12518         
12519         
12520     },
12521     
12522     compileNode : function(node, istop) {
12523         // test for
12524         //Roo.log(node);
12525         
12526         
12527         // skip anything not a tag..
12528         if (node.nodeType != 1) {
12529             if (node.nodeType == 3 && !this.inPre) {
12530                 // reduce white space..
12531                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12532                 
12533             }
12534             return;
12535         }
12536         
12537         var tpl = {
12538             uid : false,
12539             id : false,
12540             attr : false,
12541             value : false,
12542             body : '',
12543             
12544             forCall : false,
12545             execCall : false,
12546             dom : false,
12547             isTop : istop
12548             
12549             
12550         };
12551         
12552         
12553         switch(true) {
12554             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12555             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12556             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12557             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12558             // no default..
12559         }
12560         
12561         
12562         if (!tpl.attr) {
12563             // just itterate children..
12564             this.iterChild(node,this.compileNode);
12565             return;
12566         }
12567         tpl.uid = this.id++;
12568         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12569         node.removeAttribute('roo-'+ tpl.attr);
12570         if (tpl.attr != 'name') {
12571             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12572             node.parentNode.replaceChild(placeholder,  node);
12573         } else {
12574             
12575             var placeholder =  document.createElement('span');
12576             placeholder.className = 'roo-tpl-' + tpl.value;
12577             node.parentNode.replaceChild(placeholder,  node);
12578         }
12579         
12580         // parent now sees '{domtplXXXX}
12581         this.iterChild(node,this.compileNode);
12582         
12583         // we should now have node body...
12584         var div = document.createElement('div');
12585         div.appendChild(node);
12586         tpl.dom = node;
12587         // this has the unfortunate side effect of converting tagged attributes
12588         // eg. href="{...}" into %7C...%7D
12589         // this has been fixed by searching for those combo's although it's a bit hacky..
12590         
12591         
12592         tpl.body = div.innerHTML;
12593         
12594         
12595          
12596         tpl.id = tpl.uid;
12597         switch(tpl.attr) {
12598             case 'for' :
12599                 switch (tpl.value) {
12600                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12601                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12602                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12603                 }
12604                 break;
12605             
12606             case 'exec':
12607                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12608                 break;
12609             
12610             case 'if':     
12611                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12612                 break;
12613             
12614             case 'name':
12615                 tpl.id  = tpl.value; // replace non characters???
12616                 break;
12617             
12618         }
12619         
12620         
12621         this.tpls.push(tpl);
12622         
12623         
12624         
12625     },
12626     
12627     
12628     
12629     
12630     /**
12631      * Compile a segment of the template into a 'sub-template'
12632      *
12633      * 
12634      * 
12635      *
12636      */
12637     compileTpl : function(tpl)
12638     {
12639         var fm = Roo.util.Format;
12640         var useF = this.disableFormats !== true;
12641         
12642         var sep = Roo.isGecko ? "+\n" : ",\n";
12643         
12644         var undef = function(str) {
12645             Roo.debug && Roo.log("Property not found :"  + str);
12646             return '';
12647         };
12648           
12649         //Roo.log(tpl.body);
12650         
12651         
12652         
12653         var fn = function(m, lbrace, name, format, args)
12654         {
12655             //Roo.log("ARGS");
12656             //Roo.log(arguments);
12657             args = args ? args.replace(/\\'/g,"'") : args;
12658             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12659             if (typeof(format) == 'undefined') {
12660                 format =  'htmlEncode'; 
12661             }
12662             if (format == 'raw' ) {
12663                 format = false;
12664             }
12665             
12666             if(name.substr(0, 6) == 'domtpl'){
12667                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12668             }
12669             
12670             // build an array of options to determine if value is undefined..
12671             
12672             // basically get 'xxxx.yyyy' then do
12673             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12674             //    (function () { Roo.log("Property not found"); return ''; })() :
12675             //    ......
12676             
12677             var udef_ar = [];
12678             var lookfor = '';
12679             Roo.each(name.split('.'), function(st) {
12680                 lookfor += (lookfor.length ? '.': '') + st;
12681                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12682             });
12683             
12684             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12685             
12686             
12687             if(format && useF){
12688                 
12689                 args = args ? ',' + args : "";
12690                  
12691                 if(format.substr(0, 5) != "this."){
12692                     format = "fm." + format + '(';
12693                 }else{
12694                     format = 'this.call("'+ format.substr(5) + '", ';
12695                     args = ", values";
12696                 }
12697                 
12698                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12699             }
12700              
12701             if (args && args.length) {
12702                 // called with xxyx.yuu:(test,test)
12703                 // change to ()
12704                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12705             }
12706             // raw.. - :raw modifier..
12707             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12708             
12709         };
12710         var body;
12711         // branched to use + in gecko and [].join() in others
12712         if(Roo.isGecko){
12713             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12714                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12715                     "';};};";
12716         }else{
12717             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12718             body.push(tpl.body.replace(/(\r\n|\n)/g,
12719                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12720             body.push("'].join('');};};");
12721             body = body.join('');
12722         }
12723         
12724         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12725        
12726         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12727         eval(body);
12728         
12729         return this;
12730     },
12731      
12732     /**
12733      * same as applyTemplate, except it's done to one of the subTemplates
12734      * when using named templates, you can do:
12735      *
12736      * var str = pl.applySubTemplate('your-name', values);
12737      *
12738      * 
12739      * @param {Number} id of the template
12740      * @param {Object} values to apply to template
12741      * @param {Object} parent (normaly the instance of this object)
12742      */
12743     applySubTemplate : function(id, values, parent)
12744     {
12745         
12746         
12747         var t = this.tpls[id];
12748         
12749         
12750         try { 
12751             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12752                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12753                 return '';
12754             }
12755         } catch(e) {
12756             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12757             Roo.log(values);
12758           
12759             return '';
12760         }
12761         try { 
12762             
12763             if(t.execCall && t.execCall.call(this, values, parent)){
12764                 return '';
12765             }
12766         } catch(e) {
12767             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12768             Roo.log(values);
12769             return '';
12770         }
12771         
12772         try {
12773             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12774             parent = t.target ? values : parent;
12775             if(t.forCall && vs instanceof Array){
12776                 var buf = [];
12777                 for(var i = 0, len = vs.length; i < len; i++){
12778                     try {
12779                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12780                     } catch (e) {
12781                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12782                         Roo.log(e.body);
12783                         //Roo.log(t.compiled);
12784                         Roo.log(vs[i]);
12785                     }   
12786                 }
12787                 return buf.join('');
12788             }
12789         } catch (e) {
12790             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12791             Roo.log(values);
12792             return '';
12793         }
12794         try {
12795             return t.compiled.call(this, vs, parent);
12796         } catch (e) {
12797             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12798             Roo.log(e.body);
12799             //Roo.log(t.compiled);
12800             Roo.log(values);
12801             return '';
12802         }
12803     },
12804
12805    
12806
12807     applyTemplate : function(values){
12808         return this.master.compiled.call(this, values, {});
12809         //var s = this.subs;
12810     },
12811
12812     apply : function(){
12813         return this.applyTemplate.apply(this, arguments);
12814     }
12815
12816  });
12817
12818 Roo.DomTemplate.from = function(el){
12819     el = Roo.getDom(el);
12820     return new Roo.Domtemplate(el.value || el.innerHTML);
12821 };/*
12822  * Based on:
12823  * Ext JS Library 1.1.1
12824  * Copyright(c) 2006-2007, Ext JS, LLC.
12825  *
12826  * Originally Released Under LGPL - original licence link has changed is not relivant.
12827  *
12828  * Fork - LGPL
12829  * <script type="text/javascript">
12830  */
12831
12832 /**
12833  * @class Roo.util.DelayedTask
12834  * Provides a convenient method of performing setTimeout where a new
12835  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12836  * You can use this class to buffer
12837  * the keypress events for a certain number of milliseconds, and perform only if they stop
12838  * for that amount of time.
12839  * @constructor The parameters to this constructor serve as defaults and are not required.
12840  * @param {Function} fn (optional) The default function to timeout
12841  * @param {Object} scope (optional) The default scope of that timeout
12842  * @param {Array} args (optional) The default Array of arguments
12843  */
12844 Roo.util.DelayedTask = function(fn, scope, args){
12845     var id = null, d, t;
12846
12847     var call = function(){
12848         var now = new Date().getTime();
12849         if(now - t >= d){
12850             clearInterval(id);
12851             id = null;
12852             fn.apply(scope, args || []);
12853         }
12854     };
12855     /**
12856      * Cancels any pending timeout and queues a new one
12857      * @param {Number} delay The milliseconds to delay
12858      * @param {Function} newFn (optional) Overrides function passed to constructor
12859      * @param {Object} newScope (optional) Overrides scope passed to constructor
12860      * @param {Array} newArgs (optional) Overrides args passed to constructor
12861      */
12862     this.delay = function(delay, newFn, newScope, newArgs){
12863         if(id && delay != d){
12864             this.cancel();
12865         }
12866         d = delay;
12867         t = new Date().getTime();
12868         fn = newFn || fn;
12869         scope = newScope || scope;
12870         args = newArgs || args;
12871         if(!id){
12872             id = setInterval(call, d);
12873         }
12874     };
12875
12876     /**
12877      * Cancel the last queued timeout
12878      */
12879     this.cancel = function(){
12880         if(id){
12881             clearInterval(id);
12882             id = null;
12883         }
12884     };
12885 };/*
12886  * Based on:
12887  * Ext JS Library 1.1.1
12888  * Copyright(c) 2006-2007, Ext JS, LLC.
12889  *
12890  * Originally Released Under LGPL - original licence link has changed is not relivant.
12891  *
12892  * Fork - LGPL
12893  * <script type="text/javascript">
12894  */
12895  
12896  
12897 Roo.util.TaskRunner = function(interval){
12898     interval = interval || 10;
12899     var tasks = [], removeQueue = [];
12900     var id = 0;
12901     var running = false;
12902
12903     var stopThread = function(){
12904         running = false;
12905         clearInterval(id);
12906         id = 0;
12907     };
12908
12909     var startThread = function(){
12910         if(!running){
12911             running = true;
12912             id = setInterval(runTasks, interval);
12913         }
12914     };
12915
12916     var removeTask = function(task){
12917         removeQueue.push(task);
12918         if(task.onStop){
12919             task.onStop();
12920         }
12921     };
12922
12923     var runTasks = function(){
12924         if(removeQueue.length > 0){
12925             for(var i = 0, len = removeQueue.length; i < len; i++){
12926                 tasks.remove(removeQueue[i]);
12927             }
12928             removeQueue = [];
12929             if(tasks.length < 1){
12930                 stopThread();
12931                 return;
12932             }
12933         }
12934         var now = new Date().getTime();
12935         for(var i = 0, len = tasks.length; i < len; ++i){
12936             var t = tasks[i];
12937             var itime = now - t.taskRunTime;
12938             if(t.interval <= itime){
12939                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12940                 t.taskRunTime = now;
12941                 if(rt === false || t.taskRunCount === t.repeat){
12942                     removeTask(t);
12943                     return;
12944                 }
12945             }
12946             if(t.duration && t.duration <= (now - t.taskStartTime)){
12947                 removeTask(t);
12948             }
12949         }
12950     };
12951
12952     /**
12953      * Queues a new task.
12954      * @param {Object} task
12955      */
12956     this.start = function(task){
12957         tasks.push(task);
12958         task.taskStartTime = new Date().getTime();
12959         task.taskRunTime = 0;
12960         task.taskRunCount = 0;
12961         startThread();
12962         return task;
12963     };
12964
12965     this.stop = function(task){
12966         removeTask(task);
12967         return task;
12968     };
12969
12970     this.stopAll = function(){
12971         stopThread();
12972         for(var i = 0, len = tasks.length; i < len; i++){
12973             if(tasks[i].onStop){
12974                 tasks[i].onStop();
12975             }
12976         }
12977         tasks = [];
12978         removeQueue = [];
12979     };
12980 };
12981
12982 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12983  * Based on:
12984  * Ext JS Library 1.1.1
12985  * Copyright(c) 2006-2007, Ext JS, LLC.
12986  *
12987  * Originally Released Under LGPL - original licence link has changed is not relivant.
12988  *
12989  * Fork - LGPL
12990  * <script type="text/javascript">
12991  */
12992
12993  
12994 /**
12995  * @class Roo.util.MixedCollection
12996  * @extends Roo.util.Observable
12997  * A Collection class that maintains both numeric indexes and keys and exposes events.
12998  * @constructor
12999  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13000  * collection (defaults to false)
13001  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13002  * and return the key value for that item.  This is used when available to look up the key on items that
13003  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13004  * equivalent to providing an implementation for the {@link #getKey} method.
13005  */
13006 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13007     this.items = [];
13008     this.map = {};
13009     this.keys = [];
13010     this.length = 0;
13011     this.addEvents({
13012         /**
13013          * @event clear
13014          * Fires when the collection is cleared.
13015          */
13016         "clear" : true,
13017         /**
13018          * @event add
13019          * Fires when an item is added to the collection.
13020          * @param {Number} index The index at which the item was added.
13021          * @param {Object} o The item added.
13022          * @param {String} key The key associated with the added item.
13023          */
13024         "add" : true,
13025         /**
13026          * @event replace
13027          * Fires when an item is replaced in the collection.
13028          * @param {String} key he key associated with the new added.
13029          * @param {Object} old The item being replaced.
13030          * @param {Object} new The new item.
13031          */
13032         "replace" : true,
13033         /**
13034          * @event remove
13035          * Fires when an item is removed from the collection.
13036          * @param {Object} o The item being removed.
13037          * @param {String} key (optional) The key associated with the removed item.
13038          */
13039         "remove" : true,
13040         "sort" : true
13041     });
13042     this.allowFunctions = allowFunctions === true;
13043     if(keyFn){
13044         this.getKey = keyFn;
13045     }
13046     Roo.util.MixedCollection.superclass.constructor.call(this);
13047 };
13048
13049 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13050     allowFunctions : false,
13051     
13052 /**
13053  * Adds an item to the collection.
13054  * @param {String} key The key to associate with the item
13055  * @param {Object} o The item to add.
13056  * @return {Object} The item added.
13057  */
13058     add : function(key, o){
13059         if(arguments.length == 1){
13060             o = arguments[0];
13061             key = this.getKey(o);
13062         }
13063         if(typeof key == "undefined" || key === null){
13064             this.length++;
13065             this.items.push(o);
13066             this.keys.push(null);
13067         }else{
13068             var old = this.map[key];
13069             if(old){
13070                 return this.replace(key, o);
13071             }
13072             this.length++;
13073             this.items.push(o);
13074             this.map[key] = o;
13075             this.keys.push(key);
13076         }
13077         this.fireEvent("add", this.length-1, o, key);
13078         return o;
13079     },
13080        
13081 /**
13082   * MixedCollection has a generic way to fetch keys if you implement getKey.
13083 <pre><code>
13084 // normal way
13085 var mc = new Roo.util.MixedCollection();
13086 mc.add(someEl.dom.id, someEl);
13087 mc.add(otherEl.dom.id, otherEl);
13088 //and so on
13089
13090 // using getKey
13091 var mc = new Roo.util.MixedCollection();
13092 mc.getKey = function(el){
13093    return el.dom.id;
13094 };
13095 mc.add(someEl);
13096 mc.add(otherEl);
13097
13098 // or via the constructor
13099 var mc = new Roo.util.MixedCollection(false, function(el){
13100    return el.dom.id;
13101 });
13102 mc.add(someEl);
13103 mc.add(otherEl);
13104 </code></pre>
13105  * @param o {Object} The item for which to find the key.
13106  * @return {Object} The key for the passed item.
13107  */
13108     getKey : function(o){
13109          return o.id; 
13110     },
13111    
13112 /**
13113  * Replaces an item in the collection.
13114  * @param {String} key The key associated with the item to replace, or the item to replace.
13115  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13116  * @return {Object}  The new item.
13117  */
13118     replace : function(key, o){
13119         if(arguments.length == 1){
13120             o = arguments[0];
13121             key = this.getKey(o);
13122         }
13123         var old = this.item(key);
13124         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13125              return this.add(key, o);
13126         }
13127         var index = this.indexOfKey(key);
13128         this.items[index] = o;
13129         this.map[key] = o;
13130         this.fireEvent("replace", key, old, o);
13131         return o;
13132     },
13133    
13134 /**
13135  * Adds all elements of an Array or an Object to the collection.
13136  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13137  * an Array of values, each of which are added to the collection.
13138  */
13139     addAll : function(objs){
13140         if(arguments.length > 1 || objs instanceof Array){
13141             var args = arguments.length > 1 ? arguments : objs;
13142             for(var i = 0, len = args.length; i < len; i++){
13143                 this.add(args[i]);
13144             }
13145         }else{
13146             for(var key in objs){
13147                 if(this.allowFunctions || typeof objs[key] != "function"){
13148                     this.add(key, objs[key]);
13149                 }
13150             }
13151         }
13152     },
13153    
13154 /**
13155  * Executes the specified function once for every item in the collection, passing each
13156  * item as the first and only parameter. returning false from the function will stop the iteration.
13157  * @param {Function} fn The function to execute for each item.
13158  * @param {Object} scope (optional) The scope in which to execute the function.
13159  */
13160     each : function(fn, scope){
13161         var items = [].concat(this.items); // each safe for removal
13162         for(var i = 0, len = items.length; i < len; i++){
13163             if(fn.call(scope || items[i], items[i], i, len) === false){
13164                 break;
13165             }
13166         }
13167     },
13168    
13169 /**
13170  * Executes the specified function once for every key in the collection, passing each
13171  * key, and its associated item as the first two parameters.
13172  * @param {Function} fn The function to execute for each item.
13173  * @param {Object} scope (optional) The scope in which to execute the function.
13174  */
13175     eachKey : function(fn, scope){
13176         for(var i = 0, len = this.keys.length; i < len; i++){
13177             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13178         }
13179     },
13180    
13181 /**
13182  * Returns the first item in the collection which elicits a true return value from the
13183  * passed selection function.
13184  * @param {Function} fn The selection function to execute for each item.
13185  * @param {Object} scope (optional) The scope in which to execute the function.
13186  * @return {Object} The first item in the collection which returned true from the selection function.
13187  */
13188     find : function(fn, scope){
13189         for(var i = 0, len = this.items.length; i < len; i++){
13190             if(fn.call(scope || window, this.items[i], this.keys[i])){
13191                 return this.items[i];
13192             }
13193         }
13194         return null;
13195     },
13196    
13197 /**
13198  * Inserts an item at the specified index in the collection.
13199  * @param {Number} index The index to insert the item at.
13200  * @param {String} key The key to associate with the new item, or the item itself.
13201  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13202  * @return {Object} The item inserted.
13203  */
13204     insert : function(index, key, o){
13205         if(arguments.length == 2){
13206             o = arguments[1];
13207             key = this.getKey(o);
13208         }
13209         if(index >= this.length){
13210             return this.add(key, o);
13211         }
13212         this.length++;
13213         this.items.splice(index, 0, o);
13214         if(typeof key != "undefined" && key != null){
13215             this.map[key] = o;
13216         }
13217         this.keys.splice(index, 0, key);
13218         this.fireEvent("add", index, o, key);
13219         return o;
13220     },
13221    
13222 /**
13223  * Removed an item from the collection.
13224  * @param {Object} o The item to remove.
13225  * @return {Object} The item removed.
13226  */
13227     remove : function(o){
13228         return this.removeAt(this.indexOf(o));
13229     },
13230    
13231 /**
13232  * Remove an item from a specified index in the collection.
13233  * @param {Number} index The index within the collection of the item to remove.
13234  */
13235     removeAt : function(index){
13236         if(index < this.length && index >= 0){
13237             this.length--;
13238             var o = this.items[index];
13239             this.items.splice(index, 1);
13240             var key = this.keys[index];
13241             if(typeof key != "undefined"){
13242                 delete this.map[key];
13243             }
13244             this.keys.splice(index, 1);
13245             this.fireEvent("remove", o, key);
13246         }
13247     },
13248    
13249 /**
13250  * Removed an item associated with the passed key fom the collection.
13251  * @param {String} key The key of the item to remove.
13252  */
13253     removeKey : function(key){
13254         return this.removeAt(this.indexOfKey(key));
13255     },
13256    
13257 /**
13258  * Returns the number of items in the collection.
13259  * @return {Number} the number of items in the collection.
13260  */
13261     getCount : function(){
13262         return this.length; 
13263     },
13264    
13265 /**
13266  * Returns index within the collection of the passed Object.
13267  * @param {Object} o The item to find the index of.
13268  * @return {Number} index of the item.
13269  */
13270     indexOf : function(o){
13271         if(!this.items.indexOf){
13272             for(var i = 0, len = this.items.length; i < len; i++){
13273                 if(this.items[i] == o) {
13274                     return i;
13275                 }
13276             }
13277             return -1;
13278         }else{
13279             return this.items.indexOf(o);
13280         }
13281     },
13282    
13283 /**
13284  * Returns index within the collection of the passed key.
13285  * @param {String} key The key to find the index of.
13286  * @return {Number} index of the key.
13287  */
13288     indexOfKey : function(key){
13289         if(!this.keys.indexOf){
13290             for(var i = 0, len = this.keys.length; i < len; i++){
13291                 if(this.keys[i] == key) {
13292                     return i;
13293                 }
13294             }
13295             return -1;
13296         }else{
13297             return this.keys.indexOf(key);
13298         }
13299     },
13300    
13301 /**
13302  * Returns the item associated with the passed key OR index. Key has priority over index.
13303  * @param {String/Number} key The key or index of the item.
13304  * @return {Object} The item associated with the passed key.
13305  */
13306     item : function(key){
13307         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13308         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13309     },
13310     
13311 /**
13312  * Returns the item at the specified index.
13313  * @param {Number} index The index of the item.
13314  * @return {Object}
13315  */
13316     itemAt : function(index){
13317         return this.items[index];
13318     },
13319     
13320 /**
13321  * Returns the item associated with the passed key.
13322  * @param {String/Number} key The key of the item.
13323  * @return {Object} The item associated with the passed key.
13324  */
13325     key : function(key){
13326         return this.map[key];
13327     },
13328    
13329 /**
13330  * Returns true if the collection contains the passed Object as an item.
13331  * @param {Object} o  The Object to look for in the collection.
13332  * @return {Boolean} True if the collection contains the Object as an item.
13333  */
13334     contains : function(o){
13335         return this.indexOf(o) != -1;
13336     },
13337    
13338 /**
13339  * Returns true if the collection contains the passed Object as a key.
13340  * @param {String} key The key to look for in the collection.
13341  * @return {Boolean} True if the collection contains the Object as a key.
13342  */
13343     containsKey : function(key){
13344         return typeof this.map[key] != "undefined";
13345     },
13346    
13347 /**
13348  * Removes all items from the collection.
13349  */
13350     clear : function(){
13351         this.length = 0;
13352         this.items = [];
13353         this.keys = [];
13354         this.map = {};
13355         this.fireEvent("clear");
13356     },
13357    
13358 /**
13359  * Returns the first item in the collection.
13360  * @return {Object} the first item in the collection..
13361  */
13362     first : function(){
13363         return this.items[0]; 
13364     },
13365    
13366 /**
13367  * Returns the last item in the collection.
13368  * @return {Object} the last item in the collection..
13369  */
13370     last : function(){
13371         return this.items[this.length-1];   
13372     },
13373     
13374     _sort : function(property, dir, fn){
13375         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13376         fn = fn || function(a, b){
13377             return a-b;
13378         };
13379         var c = [], k = this.keys, items = this.items;
13380         for(var i = 0, len = items.length; i < len; i++){
13381             c[c.length] = {key: k[i], value: items[i], index: i};
13382         }
13383         c.sort(function(a, b){
13384             var v = fn(a[property], b[property]) * dsc;
13385             if(v == 0){
13386                 v = (a.index < b.index ? -1 : 1);
13387             }
13388             return v;
13389         });
13390         for(var i = 0, len = c.length; i < len; i++){
13391             items[i] = c[i].value;
13392             k[i] = c[i].key;
13393         }
13394         this.fireEvent("sort", this);
13395     },
13396     
13397     /**
13398      * Sorts this collection with the passed comparison function
13399      * @param {String} direction (optional) "ASC" or "DESC"
13400      * @param {Function} fn (optional) comparison function
13401      */
13402     sort : function(dir, fn){
13403         this._sort("value", dir, fn);
13404     },
13405     
13406     /**
13407      * Sorts this collection by keys
13408      * @param {String} direction (optional) "ASC" or "DESC"
13409      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13410      */
13411     keySort : function(dir, fn){
13412         this._sort("key", dir, fn || function(a, b){
13413             return String(a).toUpperCase()-String(b).toUpperCase();
13414         });
13415     },
13416     
13417     /**
13418      * Returns a range of items in this collection
13419      * @param {Number} startIndex (optional) defaults to 0
13420      * @param {Number} endIndex (optional) default to the last item
13421      * @return {Array} An array of items
13422      */
13423     getRange : function(start, end){
13424         var items = this.items;
13425         if(items.length < 1){
13426             return [];
13427         }
13428         start = start || 0;
13429         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13430         var r = [];
13431         if(start <= end){
13432             for(var i = start; i <= end; i++) {
13433                     r[r.length] = items[i];
13434             }
13435         }else{
13436             for(var i = start; i >= end; i--) {
13437                     r[r.length] = items[i];
13438             }
13439         }
13440         return r;
13441     },
13442         
13443     /**
13444      * Filter the <i>objects</i> in this collection by a specific property. 
13445      * Returns a new collection that has been filtered.
13446      * @param {String} property A property on your objects
13447      * @param {String/RegExp} value Either string that the property values 
13448      * should start with or a RegExp to test against the property
13449      * @return {MixedCollection} The new filtered collection
13450      */
13451     filter : function(property, value){
13452         if(!value.exec){ // not a regex
13453             value = String(value);
13454             if(value.length == 0){
13455                 return this.clone();
13456             }
13457             value = new RegExp("^" + Roo.escapeRe(value), "i");
13458         }
13459         return this.filterBy(function(o){
13460             return o && value.test(o[property]);
13461         });
13462         },
13463     
13464     /**
13465      * Filter by a function. * Returns a new collection that has been filtered.
13466      * The passed function will be called with each 
13467      * object in the collection. If the function returns true, the value is included 
13468      * otherwise it is filtered.
13469      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13470      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13471      * @return {MixedCollection} The new filtered collection
13472      */
13473     filterBy : function(fn, scope){
13474         var r = new Roo.util.MixedCollection();
13475         r.getKey = this.getKey;
13476         var k = this.keys, it = this.items;
13477         for(var i = 0, len = it.length; i < len; i++){
13478             if(fn.call(scope||this, it[i], k[i])){
13479                                 r.add(k[i], it[i]);
13480                         }
13481         }
13482         return r;
13483     },
13484     
13485     /**
13486      * Creates a duplicate of this collection
13487      * @return {MixedCollection}
13488      */
13489     clone : function(){
13490         var r = new Roo.util.MixedCollection();
13491         var k = this.keys, it = this.items;
13492         for(var i = 0, len = it.length; i < len; i++){
13493             r.add(k[i], it[i]);
13494         }
13495         r.getKey = this.getKey;
13496         return r;
13497     }
13498 });
13499 /**
13500  * Returns the item associated with the passed key or index.
13501  * @method
13502  * @param {String/Number} key The key or index of the item.
13503  * @return {Object} The item associated with the passed key.
13504  */
13505 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13506  * Based on:
13507  * Ext JS Library 1.1.1
13508  * Copyright(c) 2006-2007, Ext JS, LLC.
13509  *
13510  * Originally Released Under LGPL - original licence link has changed is not relivant.
13511  *
13512  * Fork - LGPL
13513  * <script type="text/javascript">
13514  */
13515 /**
13516  * @class Roo.util.JSON
13517  * Modified version of Douglas Crockford"s json.js that doesn"t
13518  * mess with the Object prototype 
13519  * http://www.json.org/js.html
13520  * @singleton
13521  */
13522 Roo.util.JSON = new (function(){
13523     var useHasOwn = {}.hasOwnProperty ? true : false;
13524     
13525     // crashes Safari in some instances
13526     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13527     
13528     var pad = function(n) {
13529         return n < 10 ? "0" + n : n;
13530     };
13531     
13532     var m = {
13533         "\b": '\\b',
13534         "\t": '\\t',
13535         "\n": '\\n',
13536         "\f": '\\f',
13537         "\r": '\\r',
13538         '"' : '\\"',
13539         "\\": '\\\\'
13540     };
13541
13542     var encodeString = function(s){
13543         if (/["\\\x00-\x1f]/.test(s)) {
13544             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13545                 var c = m[b];
13546                 if(c){
13547                     return c;
13548                 }
13549                 c = b.charCodeAt();
13550                 return "\\u00" +
13551                     Math.floor(c / 16).toString(16) +
13552                     (c % 16).toString(16);
13553             }) + '"';
13554         }
13555         return '"' + s + '"';
13556     };
13557     
13558     var encodeArray = function(o){
13559         var a = ["["], b, i, l = o.length, v;
13560             for (i = 0; i < l; i += 1) {
13561                 v = o[i];
13562                 switch (typeof v) {
13563                     case "undefined":
13564                     case "function":
13565                     case "unknown":
13566                         break;
13567                     default:
13568                         if (b) {
13569                             a.push(',');
13570                         }
13571                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13572                         b = true;
13573                 }
13574             }
13575             a.push("]");
13576             return a.join("");
13577     };
13578     
13579     var encodeDate = function(o){
13580         return '"' + o.getFullYear() + "-" +
13581                 pad(o.getMonth() + 1) + "-" +
13582                 pad(o.getDate()) + "T" +
13583                 pad(o.getHours()) + ":" +
13584                 pad(o.getMinutes()) + ":" +
13585                 pad(o.getSeconds()) + '"';
13586     };
13587     
13588     /**
13589      * Encodes an Object, Array or other value
13590      * @param {Mixed} o The variable to encode
13591      * @return {String} The JSON string
13592      */
13593     this.encode = function(o)
13594     {
13595         // should this be extended to fully wrap stringify..
13596         
13597         if(typeof o == "undefined" || o === null){
13598             return "null";
13599         }else if(o instanceof Array){
13600             return encodeArray(o);
13601         }else if(o instanceof Date){
13602             return encodeDate(o);
13603         }else if(typeof o == "string"){
13604             return encodeString(o);
13605         }else if(typeof o == "number"){
13606             return isFinite(o) ? String(o) : "null";
13607         }else if(typeof o == "boolean"){
13608             return String(o);
13609         }else {
13610             var a = ["{"], b, i, v;
13611             for (i in o) {
13612                 if(!useHasOwn || o.hasOwnProperty(i)) {
13613                     v = o[i];
13614                     switch (typeof v) {
13615                     case "undefined":
13616                     case "function":
13617                     case "unknown":
13618                         break;
13619                     default:
13620                         if(b){
13621                             a.push(',');
13622                         }
13623                         a.push(this.encode(i), ":",
13624                                 v === null ? "null" : this.encode(v));
13625                         b = true;
13626                     }
13627                 }
13628             }
13629             a.push("}");
13630             return a.join("");
13631         }
13632     };
13633     
13634     /**
13635      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13636      * @param {String} json The JSON string
13637      * @return {Object} The resulting object
13638      */
13639     this.decode = function(json){
13640         
13641         return  /** eval:var:json */ eval("(" + json + ')');
13642     };
13643 })();
13644 /** 
13645  * Shorthand for {@link Roo.util.JSON#encode}
13646  * @member Roo encode 
13647  * @method */
13648 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13649 /** 
13650  * Shorthand for {@link Roo.util.JSON#decode}
13651  * @member Roo decode 
13652  * @method */
13653 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13654 /*
13655  * Based on:
13656  * Ext JS Library 1.1.1
13657  * Copyright(c) 2006-2007, Ext JS, LLC.
13658  *
13659  * Originally Released Under LGPL - original licence link has changed is not relivant.
13660  *
13661  * Fork - LGPL
13662  * <script type="text/javascript">
13663  */
13664  
13665 /**
13666  * @class Roo.util.Format
13667  * Reusable data formatting functions
13668  * @singleton
13669  */
13670 Roo.util.Format = function(){
13671     var trimRe = /^\s+|\s+$/g;
13672     return {
13673         /**
13674          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13675          * @param {String} value The string to truncate
13676          * @param {Number} length The maximum length to allow before truncating
13677          * @return {String} The converted text
13678          */
13679         ellipsis : function(value, len){
13680             if(value && value.length > len){
13681                 return value.substr(0, len-3)+"...";
13682             }
13683             return value;
13684         },
13685
13686         /**
13687          * Checks a reference and converts it to empty string if it is undefined
13688          * @param {Mixed} value Reference to check
13689          * @return {Mixed} Empty string if converted, otherwise the original value
13690          */
13691         undef : function(value){
13692             return typeof value != "undefined" ? value : "";
13693         },
13694
13695         /**
13696          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13697          * @param {String} value The string to encode
13698          * @return {String} The encoded text
13699          */
13700         htmlEncode : function(value){
13701             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13702         },
13703
13704         /**
13705          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13706          * @param {String} value The string to decode
13707          * @return {String} The decoded text
13708          */
13709         htmlDecode : function(value){
13710             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13711         },
13712
13713         /**
13714          * Trims any whitespace from either side of a string
13715          * @param {String} value The text to trim
13716          * @return {String} The trimmed text
13717          */
13718         trim : function(value){
13719             return String(value).replace(trimRe, "");
13720         },
13721
13722         /**
13723          * Returns a substring from within an original string
13724          * @param {String} value The original text
13725          * @param {Number} start The start index of the substring
13726          * @param {Number} length The length of the substring
13727          * @return {String} The substring
13728          */
13729         substr : function(value, start, length){
13730             return String(value).substr(start, length);
13731         },
13732
13733         /**
13734          * Converts a string to all lower case letters
13735          * @param {String} value The text to convert
13736          * @return {String} The converted text
13737          */
13738         lowercase : function(value){
13739             return String(value).toLowerCase();
13740         },
13741
13742         /**
13743          * Converts a string to all upper case letters
13744          * @param {String} value The text to convert
13745          * @return {String} The converted text
13746          */
13747         uppercase : function(value){
13748             return String(value).toUpperCase();
13749         },
13750
13751         /**
13752          * Converts the first character only of a string to upper case
13753          * @param {String} value The text to convert
13754          * @return {String} The converted text
13755          */
13756         capitalize : function(value){
13757             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13758         },
13759
13760         // private
13761         call : function(value, fn){
13762             if(arguments.length > 2){
13763                 var args = Array.prototype.slice.call(arguments, 2);
13764                 args.unshift(value);
13765                  
13766                 return /** eval:var:value */  eval(fn).apply(window, args);
13767             }else{
13768                 /** eval:var:value */
13769                 return /** eval:var:value */ eval(fn).call(window, value);
13770             }
13771         },
13772
13773        
13774         /**
13775          * safer version of Math.toFixed..??/
13776          * @param {Number/String} value The numeric value to format
13777          * @param {Number/String} value Decimal places 
13778          * @return {String} The formatted currency string
13779          */
13780         toFixed : function(v, n)
13781         {
13782             // why not use to fixed - precision is buggered???
13783             if (!n) {
13784                 return Math.round(v-0);
13785             }
13786             var fact = Math.pow(10,n+1);
13787             v = (Math.round((v-0)*fact))/fact;
13788             var z = (''+fact).substring(2);
13789             if (v == Math.floor(v)) {
13790                 return Math.floor(v) + '.' + z;
13791             }
13792             
13793             // now just padd decimals..
13794             var ps = String(v).split('.');
13795             var fd = (ps[1] + z);
13796             var r = fd.substring(0,n); 
13797             var rm = fd.substring(n); 
13798             if (rm < 5) {
13799                 return ps[0] + '.' + r;
13800             }
13801             r*=1; // turn it into a number;
13802             r++;
13803             if (String(r).length != n) {
13804                 ps[0]*=1;
13805                 ps[0]++;
13806                 r = String(r).substring(1); // chop the end off.
13807             }
13808             
13809             return ps[0] + '.' + r;
13810              
13811         },
13812         
13813         /**
13814          * Format a number as US currency
13815          * @param {Number/String} value The numeric value to format
13816          * @return {String} The formatted currency string
13817          */
13818         usMoney : function(v){
13819             return '$' + Roo.util.Format.number(v);
13820         },
13821         
13822         /**
13823          * Format a number
13824          * eventually this should probably emulate php's number_format
13825          * @param {Number/String} value The numeric value to format
13826          * @param {Number} decimals number of decimal places
13827          * @param {String} delimiter for thousands (default comma)
13828          * @return {String} The formatted currency string
13829          */
13830         number : function(v, decimals, thousandsDelimiter)
13831         {
13832             // multiply and round.
13833             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13834             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13835             
13836             var mul = Math.pow(10, decimals);
13837             var zero = String(mul).substring(1);
13838             v = (Math.round((v-0)*mul))/mul;
13839             
13840             // if it's '0' number.. then
13841             
13842             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13843             v = String(v);
13844             var ps = v.split('.');
13845             var whole = ps[0];
13846             
13847             var r = /(\d+)(\d{3})/;
13848             // add comma's
13849             
13850             if(thousandsDelimiter.length != 0) {
13851                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13852             } 
13853             
13854             var sub = ps[1] ?
13855                     // has decimals..
13856                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13857                     // does not have decimals
13858                     (decimals ? ('.' + zero) : '');
13859             
13860             
13861             return whole + sub ;
13862         },
13863         
13864         /**
13865          * Parse a value into a formatted date using the specified format pattern.
13866          * @param {Mixed} value The value to format
13867          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13868          * @return {String} The formatted date string
13869          */
13870         date : function(v, format){
13871             if(!v){
13872                 return "";
13873             }
13874             if(!(v instanceof Date)){
13875                 v = new Date(Date.parse(v));
13876             }
13877             return v.dateFormat(format || Roo.util.Format.defaults.date);
13878         },
13879
13880         /**
13881          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13882          * @param {String} format Any valid date format string
13883          * @return {Function} The date formatting function
13884          */
13885         dateRenderer : function(format){
13886             return function(v){
13887                 return Roo.util.Format.date(v, format);  
13888             };
13889         },
13890
13891         // private
13892         stripTagsRE : /<\/?[^>]+>/gi,
13893         
13894         /**
13895          * Strips all HTML tags
13896          * @param {Mixed} value The text from which to strip tags
13897          * @return {String} The stripped text
13898          */
13899         stripTags : function(v){
13900             return !v ? v : String(v).replace(this.stripTagsRE, "");
13901         }
13902     };
13903 }();
13904 Roo.util.Format.defaults = {
13905     date : 'd/M/Y'
13906 };/*
13907  * Based on:
13908  * Ext JS Library 1.1.1
13909  * Copyright(c) 2006-2007, Ext JS, LLC.
13910  *
13911  * Originally Released Under LGPL - original licence link has changed is not relivant.
13912  *
13913  * Fork - LGPL
13914  * <script type="text/javascript">
13915  */
13916
13917
13918  
13919
13920 /**
13921  * @class Roo.MasterTemplate
13922  * @extends Roo.Template
13923  * Provides a template that can have child templates. The syntax is:
13924 <pre><code>
13925 var t = new Roo.MasterTemplate(
13926         '&lt;select name="{name}"&gt;',
13927                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13928         '&lt;/select&gt;'
13929 );
13930 t.add('options', {value: 'foo', text: 'bar'});
13931 // or you can add multiple child elements in one shot
13932 t.addAll('options', [
13933     {value: 'foo', text: 'bar'},
13934     {value: 'foo2', text: 'bar2'},
13935     {value: 'foo3', text: 'bar3'}
13936 ]);
13937 // then append, applying the master template values
13938 t.append('my-form', {name: 'my-select'});
13939 </code></pre>
13940 * A name attribute for the child template is not required if you have only one child
13941 * template or you want to refer to them by index.
13942  */
13943 Roo.MasterTemplate = function(){
13944     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13945     this.originalHtml = this.html;
13946     var st = {};
13947     var m, re = this.subTemplateRe;
13948     re.lastIndex = 0;
13949     var subIndex = 0;
13950     while(m = re.exec(this.html)){
13951         var name = m[1], content = m[2];
13952         st[subIndex] = {
13953             name: name,
13954             index: subIndex,
13955             buffer: [],
13956             tpl : new Roo.Template(content)
13957         };
13958         if(name){
13959             st[name] = st[subIndex];
13960         }
13961         st[subIndex].tpl.compile();
13962         st[subIndex].tpl.call = this.call.createDelegate(this);
13963         subIndex++;
13964     }
13965     this.subCount = subIndex;
13966     this.subs = st;
13967 };
13968 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13969     /**
13970     * The regular expression used to match sub templates
13971     * @type RegExp
13972     * @property
13973     */
13974     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13975
13976     /**
13977      * Applies the passed values to a child template.
13978      * @param {String/Number} name (optional) The name or index of the child template
13979      * @param {Array/Object} values The values to be applied to the template
13980      * @return {MasterTemplate} this
13981      */
13982      add : function(name, values){
13983         if(arguments.length == 1){
13984             values = arguments[0];
13985             name = 0;
13986         }
13987         var s = this.subs[name];
13988         s.buffer[s.buffer.length] = s.tpl.apply(values);
13989         return this;
13990     },
13991
13992     /**
13993      * Applies all the passed values to a child template.
13994      * @param {String/Number} name (optional) The name or index of the child template
13995      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13996      * @param {Boolean} reset (optional) True to reset the template first
13997      * @return {MasterTemplate} this
13998      */
13999     fill : function(name, values, reset){
14000         var a = arguments;
14001         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14002             values = a[0];
14003             name = 0;
14004             reset = a[1];
14005         }
14006         if(reset){
14007             this.reset();
14008         }
14009         for(var i = 0, len = values.length; i < len; i++){
14010             this.add(name, values[i]);
14011         }
14012         return this;
14013     },
14014
14015     /**
14016      * Resets the template for reuse
14017      * @return {MasterTemplate} this
14018      */
14019      reset : function(){
14020         var s = this.subs;
14021         for(var i = 0; i < this.subCount; i++){
14022             s[i].buffer = [];
14023         }
14024         return this;
14025     },
14026
14027     applyTemplate : function(values){
14028         var s = this.subs;
14029         var replaceIndex = -1;
14030         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14031             return s[++replaceIndex].buffer.join("");
14032         });
14033         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14034     },
14035
14036     apply : function(){
14037         return this.applyTemplate.apply(this, arguments);
14038     },
14039
14040     compile : function(){return this;}
14041 });
14042
14043 /**
14044  * Alias for fill().
14045  * @method
14046  */
14047 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14048  /**
14049  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14050  * var tpl = Roo.MasterTemplate.from('element-id');
14051  * @param {String/HTMLElement} el
14052  * @param {Object} config
14053  * @static
14054  */
14055 Roo.MasterTemplate.from = function(el, config){
14056     el = Roo.getDom(el);
14057     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14058 };/*
14059  * Based on:
14060  * Ext JS Library 1.1.1
14061  * Copyright(c) 2006-2007, Ext JS, LLC.
14062  *
14063  * Originally Released Under LGPL - original licence link has changed is not relivant.
14064  *
14065  * Fork - LGPL
14066  * <script type="text/javascript">
14067  */
14068
14069  
14070 /**
14071  * @class Roo.util.CSS
14072  * Utility class for manipulating CSS rules
14073  * @singleton
14074  */
14075 Roo.util.CSS = function(){
14076         var rules = null;
14077         var doc = document;
14078
14079     var camelRe = /(-[a-z])/gi;
14080     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14081
14082    return {
14083    /**
14084     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14085     * tag and appended to the HEAD of the document.
14086     * @param {String|Object} cssText The text containing the css rules
14087     * @param {String} id An id to add to the stylesheet for later removal
14088     * @return {StyleSheet}
14089     */
14090     createStyleSheet : function(cssText, id){
14091         var ss;
14092         var head = doc.getElementsByTagName("head")[0];
14093         var nrules = doc.createElement("style");
14094         nrules.setAttribute("type", "text/css");
14095         if(id){
14096             nrules.setAttribute("id", id);
14097         }
14098         if (typeof(cssText) != 'string') {
14099             // support object maps..
14100             // not sure if this a good idea.. 
14101             // perhaps it should be merged with the general css handling
14102             // and handle js style props.
14103             var cssTextNew = [];
14104             for(var n in cssText) {
14105                 var citems = [];
14106                 for(var k in cssText[n]) {
14107                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14108                 }
14109                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14110                 
14111             }
14112             cssText = cssTextNew.join("\n");
14113             
14114         }
14115        
14116        
14117        if(Roo.isIE){
14118            head.appendChild(nrules);
14119            ss = nrules.styleSheet;
14120            ss.cssText = cssText;
14121        }else{
14122            try{
14123                 nrules.appendChild(doc.createTextNode(cssText));
14124            }catch(e){
14125                nrules.cssText = cssText; 
14126            }
14127            head.appendChild(nrules);
14128            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14129        }
14130        this.cacheStyleSheet(ss);
14131        return ss;
14132    },
14133
14134    /**
14135     * Removes a style or link tag by id
14136     * @param {String} id The id of the tag
14137     */
14138    removeStyleSheet : function(id){
14139        var existing = doc.getElementById(id);
14140        if(existing){
14141            existing.parentNode.removeChild(existing);
14142        }
14143    },
14144
14145    /**
14146     * Dynamically swaps an existing stylesheet reference for a new one
14147     * @param {String} id The id of an existing link tag to remove
14148     * @param {String} url The href of the new stylesheet to include
14149     */
14150    swapStyleSheet : function(id, url){
14151        this.removeStyleSheet(id);
14152        var ss = doc.createElement("link");
14153        ss.setAttribute("rel", "stylesheet");
14154        ss.setAttribute("type", "text/css");
14155        ss.setAttribute("id", id);
14156        ss.setAttribute("href", url);
14157        doc.getElementsByTagName("head")[0].appendChild(ss);
14158    },
14159    
14160    /**
14161     * Refresh the rule cache if you have dynamically added stylesheets
14162     * @return {Object} An object (hash) of rules indexed by selector
14163     */
14164    refreshCache : function(){
14165        return this.getRules(true);
14166    },
14167
14168    // private
14169    cacheStyleSheet : function(stylesheet){
14170        if(!rules){
14171            rules = {};
14172        }
14173        try{// try catch for cross domain access issue
14174            var ssRules = stylesheet.cssRules || stylesheet.rules;
14175            for(var j = ssRules.length-1; j >= 0; --j){
14176                rules[ssRules[j].selectorText] = ssRules[j];
14177            }
14178        }catch(e){}
14179    },
14180    
14181    /**
14182     * Gets all css rules for the document
14183     * @param {Boolean} refreshCache true to refresh the internal cache
14184     * @return {Object} An object (hash) of rules indexed by selector
14185     */
14186    getRules : function(refreshCache){
14187                 if(rules == null || refreshCache){
14188                         rules = {};
14189                         var ds = doc.styleSheets;
14190                         for(var i =0, len = ds.length; i < len; i++){
14191                             try{
14192                         this.cacheStyleSheet(ds[i]);
14193                     }catch(e){} 
14194                 }
14195                 }
14196                 return rules;
14197         },
14198         
14199         /**
14200     * Gets an an individual CSS rule by selector(s)
14201     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14202     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14203     * @return {CSSRule} The CSS rule or null if one is not found
14204     */
14205    getRule : function(selector, refreshCache){
14206                 var rs = this.getRules(refreshCache);
14207                 if(!(selector instanceof Array)){
14208                     return rs[selector];
14209                 }
14210                 for(var i = 0; i < selector.length; i++){
14211                         if(rs[selector[i]]){
14212                                 return rs[selector[i]];
14213                         }
14214                 }
14215                 return null;
14216         },
14217         
14218         
14219         /**
14220     * Updates a rule property
14221     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14222     * @param {String} property The css property
14223     * @param {String} value The new value for the property
14224     * @return {Boolean} true If a rule was found and updated
14225     */
14226    updateRule : function(selector, property, value){
14227                 if(!(selector instanceof Array)){
14228                         var rule = this.getRule(selector);
14229                         if(rule){
14230                                 rule.style[property.replace(camelRe, camelFn)] = value;
14231                                 return true;
14232                         }
14233                 }else{
14234                         for(var i = 0; i < selector.length; i++){
14235                                 if(this.updateRule(selector[i], property, value)){
14236                                         return true;
14237                                 }
14238                         }
14239                 }
14240                 return false;
14241         }
14242    };   
14243 }();/*
14244  * Based on:
14245  * Ext JS Library 1.1.1
14246  * Copyright(c) 2006-2007, Ext JS, LLC.
14247  *
14248  * Originally Released Under LGPL - original licence link has changed is not relivant.
14249  *
14250  * Fork - LGPL
14251  * <script type="text/javascript">
14252  */
14253
14254  
14255
14256 /**
14257  * @class Roo.util.ClickRepeater
14258  * @extends Roo.util.Observable
14259  * 
14260  * A wrapper class which can be applied to any element. Fires a "click" event while the
14261  * mouse is pressed. The interval between firings may be specified in the config but
14262  * defaults to 10 milliseconds.
14263  * 
14264  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14265  * 
14266  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14267  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14268  * Similar to an autorepeat key delay.
14269  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14270  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14271  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14272  *           "interval" and "delay" are ignored. "immediate" is honored.
14273  * @cfg {Boolean} preventDefault True to prevent the default click event
14274  * @cfg {Boolean} stopDefault True to stop the default click event
14275  * 
14276  * @history
14277  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14278  *     2007-02-02 jvs Renamed to ClickRepeater
14279  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14280  *
14281  *  @constructor
14282  * @param {String/HTMLElement/Element} el The element to listen on
14283  * @param {Object} config
14284  **/
14285 Roo.util.ClickRepeater = function(el, config)
14286 {
14287     this.el = Roo.get(el);
14288     this.el.unselectable();
14289
14290     Roo.apply(this, config);
14291
14292     this.addEvents({
14293     /**
14294      * @event mousedown
14295      * Fires when the mouse button is depressed.
14296      * @param {Roo.util.ClickRepeater} this
14297      */
14298         "mousedown" : true,
14299     /**
14300      * @event click
14301      * Fires on a specified interval during the time the element is pressed.
14302      * @param {Roo.util.ClickRepeater} this
14303      */
14304         "click" : true,
14305     /**
14306      * @event mouseup
14307      * Fires when the mouse key is released.
14308      * @param {Roo.util.ClickRepeater} this
14309      */
14310         "mouseup" : true
14311     });
14312
14313     this.el.on("mousedown", this.handleMouseDown, this);
14314     if(this.preventDefault || this.stopDefault){
14315         this.el.on("click", function(e){
14316             if(this.preventDefault){
14317                 e.preventDefault();
14318             }
14319             if(this.stopDefault){
14320                 e.stopEvent();
14321             }
14322         }, this);
14323     }
14324
14325     // allow inline handler
14326     if(this.handler){
14327         this.on("click", this.handler,  this.scope || this);
14328     }
14329
14330     Roo.util.ClickRepeater.superclass.constructor.call(this);
14331 };
14332
14333 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14334     interval : 20,
14335     delay: 250,
14336     preventDefault : true,
14337     stopDefault : false,
14338     timer : 0,
14339
14340     // private
14341     handleMouseDown : function(){
14342         clearTimeout(this.timer);
14343         this.el.blur();
14344         if(this.pressClass){
14345             this.el.addClass(this.pressClass);
14346         }
14347         this.mousedownTime = new Date();
14348
14349         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14350         this.el.on("mouseout", this.handleMouseOut, this);
14351
14352         this.fireEvent("mousedown", this);
14353         this.fireEvent("click", this);
14354         
14355         this.timer = this.click.defer(this.delay || this.interval, this);
14356     },
14357
14358     // private
14359     click : function(){
14360         this.fireEvent("click", this);
14361         this.timer = this.click.defer(this.getInterval(), this);
14362     },
14363
14364     // private
14365     getInterval: function(){
14366         if(!this.accelerate){
14367             return this.interval;
14368         }
14369         var pressTime = this.mousedownTime.getElapsed();
14370         if(pressTime < 500){
14371             return 400;
14372         }else if(pressTime < 1700){
14373             return 320;
14374         }else if(pressTime < 2600){
14375             return 250;
14376         }else if(pressTime < 3500){
14377             return 180;
14378         }else if(pressTime < 4400){
14379             return 140;
14380         }else if(pressTime < 5300){
14381             return 80;
14382         }else if(pressTime < 6200){
14383             return 50;
14384         }else{
14385             return 10;
14386         }
14387     },
14388
14389     // private
14390     handleMouseOut : function(){
14391         clearTimeout(this.timer);
14392         if(this.pressClass){
14393             this.el.removeClass(this.pressClass);
14394         }
14395         this.el.on("mouseover", this.handleMouseReturn, this);
14396     },
14397
14398     // private
14399     handleMouseReturn : function(){
14400         this.el.un("mouseover", this.handleMouseReturn);
14401         if(this.pressClass){
14402             this.el.addClass(this.pressClass);
14403         }
14404         this.click();
14405     },
14406
14407     // private
14408     handleMouseUp : function(){
14409         clearTimeout(this.timer);
14410         this.el.un("mouseover", this.handleMouseReturn);
14411         this.el.un("mouseout", this.handleMouseOut);
14412         Roo.get(document).un("mouseup", this.handleMouseUp);
14413         this.el.removeClass(this.pressClass);
14414         this.fireEvent("mouseup", this);
14415     }
14416 });/*
14417  * Based on:
14418  * Ext JS Library 1.1.1
14419  * Copyright(c) 2006-2007, Ext JS, LLC.
14420  *
14421  * Originally Released Under LGPL - original licence link has changed is not relivant.
14422  *
14423  * Fork - LGPL
14424  * <script type="text/javascript">
14425  */
14426
14427  
14428 /**
14429  * @class Roo.KeyNav
14430  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14431  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14432  * way to implement custom navigation schemes for any UI component.</p>
14433  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14434  * pageUp, pageDown, del, home, end.  Usage:</p>
14435  <pre><code>
14436 var nav = new Roo.KeyNav("my-element", {
14437     "left" : function(e){
14438         this.moveLeft(e.ctrlKey);
14439     },
14440     "right" : function(e){
14441         this.moveRight(e.ctrlKey);
14442     },
14443     "enter" : function(e){
14444         this.save();
14445     },
14446     scope : this
14447 });
14448 </code></pre>
14449  * @constructor
14450  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14451  * @param {Object} config The config
14452  */
14453 Roo.KeyNav = function(el, config){
14454     this.el = Roo.get(el);
14455     Roo.apply(this, config);
14456     if(!this.disabled){
14457         this.disabled = true;
14458         this.enable();
14459     }
14460 };
14461
14462 Roo.KeyNav.prototype = {
14463     /**
14464      * @cfg {Boolean} disabled
14465      * True to disable this KeyNav instance (defaults to false)
14466      */
14467     disabled : false,
14468     /**
14469      * @cfg {String} defaultEventAction
14470      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14471      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14472      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14473      */
14474     defaultEventAction: "stopEvent",
14475     /**
14476      * @cfg {Boolean} forceKeyDown
14477      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14478      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14479      * handle keydown instead of keypress.
14480      */
14481     forceKeyDown : false,
14482
14483     // private
14484     prepareEvent : function(e){
14485         var k = e.getKey();
14486         var h = this.keyToHandler[k];
14487         //if(h && this[h]){
14488         //    e.stopPropagation();
14489         //}
14490         if(Roo.isSafari && h && k >= 37 && k <= 40){
14491             e.stopEvent();
14492         }
14493     },
14494
14495     // private
14496     relay : function(e){
14497         var k = e.getKey();
14498         var h = this.keyToHandler[k];
14499         if(h && this[h]){
14500             if(this.doRelay(e, this[h], h) !== true){
14501                 e[this.defaultEventAction]();
14502             }
14503         }
14504     },
14505
14506     // private
14507     doRelay : function(e, h, hname){
14508         return h.call(this.scope || this, e);
14509     },
14510
14511     // possible handlers
14512     enter : false,
14513     left : false,
14514     right : false,
14515     up : false,
14516     down : false,
14517     tab : false,
14518     esc : false,
14519     pageUp : false,
14520     pageDown : false,
14521     del : false,
14522     home : false,
14523     end : false,
14524
14525     // quick lookup hash
14526     keyToHandler : {
14527         37 : "left",
14528         39 : "right",
14529         38 : "up",
14530         40 : "down",
14531         33 : "pageUp",
14532         34 : "pageDown",
14533         46 : "del",
14534         36 : "home",
14535         35 : "end",
14536         13 : "enter",
14537         27 : "esc",
14538         9  : "tab"
14539     },
14540
14541         /**
14542          * Enable this KeyNav
14543          */
14544         enable: function(){
14545                 if(this.disabled){
14546             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14547             // the EventObject will normalize Safari automatically
14548             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14549                 this.el.on("keydown", this.relay,  this);
14550             }else{
14551                 this.el.on("keydown", this.prepareEvent,  this);
14552                 this.el.on("keypress", this.relay,  this);
14553             }
14554                     this.disabled = false;
14555                 }
14556         },
14557
14558         /**
14559          * Disable this KeyNav
14560          */
14561         disable: function(){
14562                 if(!this.disabled){
14563                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14564                 this.el.un("keydown", this.relay);
14565             }else{
14566                 this.el.un("keydown", this.prepareEvent);
14567                 this.el.un("keypress", this.relay);
14568             }
14569                     this.disabled = true;
14570                 }
14571         }
14572 };/*
14573  * Based on:
14574  * Ext JS Library 1.1.1
14575  * Copyright(c) 2006-2007, Ext JS, LLC.
14576  *
14577  * Originally Released Under LGPL - original licence link has changed is not relivant.
14578  *
14579  * Fork - LGPL
14580  * <script type="text/javascript">
14581  */
14582
14583  
14584 /**
14585  * @class Roo.KeyMap
14586  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14587  * The constructor accepts the same config object as defined by {@link #addBinding}.
14588  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14589  * combination it will call the function with this signature (if the match is a multi-key
14590  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14591  * A KeyMap can also handle a string representation of keys.<br />
14592  * Usage:
14593  <pre><code>
14594 // map one key by key code
14595 var map = new Roo.KeyMap("my-element", {
14596     key: 13, // or Roo.EventObject.ENTER
14597     fn: myHandler,
14598     scope: myObject
14599 });
14600
14601 // map multiple keys to one action by string
14602 var map = new Roo.KeyMap("my-element", {
14603     key: "a\r\n\t",
14604     fn: myHandler,
14605     scope: myObject
14606 });
14607
14608 // map multiple keys to multiple actions by strings and array of codes
14609 var map = new Roo.KeyMap("my-element", [
14610     {
14611         key: [10,13],
14612         fn: function(){ alert("Return was pressed"); }
14613     }, {
14614         key: "abc",
14615         fn: function(){ alert('a, b or c was pressed'); }
14616     }, {
14617         key: "\t",
14618         ctrl:true,
14619         shift:true,
14620         fn: function(){ alert('Control + shift + tab was pressed.'); }
14621     }
14622 ]);
14623 </code></pre>
14624  * <b>Note: A KeyMap starts enabled</b>
14625  * @constructor
14626  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14627  * @param {Object} config The config (see {@link #addBinding})
14628  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14629  */
14630 Roo.KeyMap = function(el, config, eventName){
14631     this.el  = Roo.get(el);
14632     this.eventName = eventName || "keydown";
14633     this.bindings = [];
14634     if(config){
14635         this.addBinding(config);
14636     }
14637     this.enable();
14638 };
14639
14640 Roo.KeyMap.prototype = {
14641     /**
14642      * True to stop the event from bubbling and prevent the default browser action if the
14643      * key was handled by the KeyMap (defaults to false)
14644      * @type Boolean
14645      */
14646     stopEvent : false,
14647
14648     /**
14649      * Add a new binding to this KeyMap. The following config object properties are supported:
14650      * <pre>
14651 Property    Type             Description
14652 ----------  ---------------  ----------------------------------------------------------------------
14653 key         String/Array     A single keycode or an array of keycodes to handle
14654 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14655 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14656 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14657 fn          Function         The function to call when KeyMap finds the expected key combination
14658 scope       Object           The scope of the callback function
14659 </pre>
14660      *
14661      * Usage:
14662      * <pre><code>
14663 // Create a KeyMap
14664 var map = new Roo.KeyMap(document, {
14665     key: Roo.EventObject.ENTER,
14666     fn: handleKey,
14667     scope: this
14668 });
14669
14670 //Add a new binding to the existing KeyMap later
14671 map.addBinding({
14672     key: 'abc',
14673     shift: true,
14674     fn: handleKey,
14675     scope: this
14676 });
14677 </code></pre>
14678      * @param {Object/Array} config A single KeyMap config or an array of configs
14679      */
14680         addBinding : function(config){
14681         if(config instanceof Array){
14682             for(var i = 0, len = config.length; i < len; i++){
14683                 this.addBinding(config[i]);
14684             }
14685             return;
14686         }
14687         var keyCode = config.key,
14688             shift = config.shift, 
14689             ctrl = config.ctrl, 
14690             alt = config.alt,
14691             fn = config.fn,
14692             scope = config.scope;
14693         if(typeof keyCode == "string"){
14694             var ks = [];
14695             var keyString = keyCode.toUpperCase();
14696             for(var j = 0, len = keyString.length; j < len; j++){
14697                 ks.push(keyString.charCodeAt(j));
14698             }
14699             keyCode = ks;
14700         }
14701         var keyArray = keyCode instanceof Array;
14702         var handler = function(e){
14703             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14704                 var k = e.getKey();
14705                 if(keyArray){
14706                     for(var i = 0, len = keyCode.length; i < len; i++){
14707                         if(keyCode[i] == k){
14708                           if(this.stopEvent){
14709                               e.stopEvent();
14710                           }
14711                           fn.call(scope || window, k, e);
14712                           return;
14713                         }
14714                     }
14715                 }else{
14716                     if(k == keyCode){
14717                         if(this.stopEvent){
14718                            e.stopEvent();
14719                         }
14720                         fn.call(scope || window, k, e);
14721                     }
14722                 }
14723             }
14724         };
14725         this.bindings.push(handler);  
14726         },
14727
14728     /**
14729      * Shorthand for adding a single key listener
14730      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14731      * following options:
14732      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14733      * @param {Function} fn The function to call
14734      * @param {Object} scope (optional) The scope of the function
14735      */
14736     on : function(key, fn, scope){
14737         var keyCode, shift, ctrl, alt;
14738         if(typeof key == "object" && !(key instanceof Array)){
14739             keyCode = key.key;
14740             shift = key.shift;
14741             ctrl = key.ctrl;
14742             alt = key.alt;
14743         }else{
14744             keyCode = key;
14745         }
14746         this.addBinding({
14747             key: keyCode,
14748             shift: shift,
14749             ctrl: ctrl,
14750             alt: alt,
14751             fn: fn,
14752             scope: scope
14753         })
14754     },
14755
14756     // private
14757     handleKeyDown : function(e){
14758             if(this.enabled){ //just in case
14759             var b = this.bindings;
14760             for(var i = 0, len = b.length; i < len; i++){
14761                 b[i].call(this, e);
14762             }
14763             }
14764         },
14765         
14766         /**
14767          * Returns true if this KeyMap is enabled
14768          * @return {Boolean} 
14769          */
14770         isEnabled : function(){
14771             return this.enabled;  
14772         },
14773         
14774         /**
14775          * Enables this KeyMap
14776          */
14777         enable: function(){
14778                 if(!this.enabled){
14779                     this.el.on(this.eventName, this.handleKeyDown, this);
14780                     this.enabled = true;
14781                 }
14782         },
14783
14784         /**
14785          * Disable this KeyMap
14786          */
14787         disable: function(){
14788                 if(this.enabled){
14789                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14790                     this.enabled = false;
14791                 }
14792         }
14793 };/*
14794  * Based on:
14795  * Ext JS Library 1.1.1
14796  * Copyright(c) 2006-2007, Ext JS, LLC.
14797  *
14798  * Originally Released Under LGPL - original licence link has changed is not relivant.
14799  *
14800  * Fork - LGPL
14801  * <script type="text/javascript">
14802  */
14803
14804  
14805 /**
14806  * @class Roo.util.TextMetrics
14807  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14808  * wide, in pixels, a given block of text will be.
14809  * @singleton
14810  */
14811 Roo.util.TextMetrics = function(){
14812     var shared;
14813     return {
14814         /**
14815          * Measures the size of the specified text
14816          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14817          * that can affect the size of the rendered text
14818          * @param {String} text The text to measure
14819          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14820          * in order to accurately measure the text height
14821          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14822          */
14823         measure : function(el, text, fixedWidth){
14824             if(!shared){
14825                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14826             }
14827             shared.bind(el);
14828             shared.setFixedWidth(fixedWidth || 'auto');
14829             return shared.getSize(text);
14830         },
14831
14832         /**
14833          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14834          * the overhead of multiple calls to initialize the style properties on each measurement.
14835          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14836          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14837          * in order to accurately measure the text height
14838          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14839          */
14840         createInstance : function(el, fixedWidth){
14841             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14842         }
14843     };
14844 }();
14845
14846  
14847
14848 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14849     var ml = new Roo.Element(document.createElement('div'));
14850     document.body.appendChild(ml.dom);
14851     ml.position('absolute');
14852     ml.setLeftTop(-1000, -1000);
14853     ml.hide();
14854
14855     if(fixedWidth){
14856         ml.setWidth(fixedWidth);
14857     }
14858      
14859     var instance = {
14860         /**
14861          * Returns the size of the specified text based on the internal element's style and width properties
14862          * @memberOf Roo.util.TextMetrics.Instance#
14863          * @param {String} text The text to measure
14864          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14865          */
14866         getSize : function(text){
14867             ml.update(text);
14868             var s = ml.getSize();
14869             ml.update('');
14870             return s;
14871         },
14872
14873         /**
14874          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14875          * that can affect the size of the rendered text
14876          * @memberOf Roo.util.TextMetrics.Instance#
14877          * @param {String/HTMLElement} el The element, dom node or id
14878          */
14879         bind : function(el){
14880             ml.setStyle(
14881                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14882             );
14883         },
14884
14885         /**
14886          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14887          * to set a fixed width in order to accurately measure the text height.
14888          * @memberOf Roo.util.TextMetrics.Instance#
14889          * @param {Number} width The width to set on the element
14890          */
14891         setFixedWidth : function(width){
14892             ml.setWidth(width);
14893         },
14894
14895         /**
14896          * Returns the measured width of the specified text
14897          * @memberOf Roo.util.TextMetrics.Instance#
14898          * @param {String} text The text to measure
14899          * @return {Number} width The width in pixels
14900          */
14901         getWidth : function(text){
14902             ml.dom.style.width = 'auto';
14903             return this.getSize(text).width;
14904         },
14905
14906         /**
14907          * Returns the measured height of the specified text.  For multiline text, be sure to call
14908          * {@link #setFixedWidth} if necessary.
14909          * @memberOf Roo.util.TextMetrics.Instance#
14910          * @param {String} text The text to measure
14911          * @return {Number} height The height in pixels
14912          */
14913         getHeight : function(text){
14914             return this.getSize(text).height;
14915         }
14916     };
14917
14918     instance.bind(bindTo);
14919
14920     return instance;
14921 };
14922
14923 // backwards compat
14924 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14925  * Based on:
14926  * Ext JS Library 1.1.1
14927  * Copyright(c) 2006-2007, Ext JS, LLC.
14928  *
14929  * Originally Released Under LGPL - original licence link has changed is not relivant.
14930  *
14931  * Fork - LGPL
14932  * <script type="text/javascript">
14933  */
14934
14935 /**
14936  * @class Roo.state.Provider
14937  * Abstract base class for state provider implementations. This class provides methods
14938  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14939  * Provider interface.
14940  */
14941 Roo.state.Provider = function(){
14942     /**
14943      * @event statechange
14944      * Fires when a state change occurs.
14945      * @param {Provider} this This state provider
14946      * @param {String} key The state key which was changed
14947      * @param {String} value The encoded value for the state
14948      */
14949     this.addEvents({
14950         "statechange": true
14951     });
14952     this.state = {};
14953     Roo.state.Provider.superclass.constructor.call(this);
14954 };
14955 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14956     /**
14957      * Returns the current value for a key
14958      * @param {String} name The key name
14959      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14960      * @return {Mixed} The state data
14961      */
14962     get : function(name, defaultValue){
14963         return typeof this.state[name] == "undefined" ?
14964             defaultValue : this.state[name];
14965     },
14966     
14967     /**
14968      * Clears a value from the state
14969      * @param {String} name The key name
14970      */
14971     clear : function(name){
14972         delete this.state[name];
14973         this.fireEvent("statechange", this, name, null);
14974     },
14975     
14976     /**
14977      * Sets the value for a key
14978      * @param {String} name The key name
14979      * @param {Mixed} value The value to set
14980      */
14981     set : function(name, value){
14982         this.state[name] = value;
14983         this.fireEvent("statechange", this, name, value);
14984     },
14985     
14986     /**
14987      * Decodes a string previously encoded with {@link #encodeValue}.
14988      * @param {String} value The value to decode
14989      * @return {Mixed} The decoded value
14990      */
14991     decodeValue : function(cookie){
14992         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14993         var matches = re.exec(unescape(cookie));
14994         if(!matches || !matches[1]) {
14995             return; // non state cookie
14996         }
14997         var type = matches[1];
14998         var v = matches[2];
14999         switch(type){
15000             case "n":
15001                 return parseFloat(v);
15002             case "d":
15003                 return new Date(Date.parse(v));
15004             case "b":
15005                 return (v == "1");
15006             case "a":
15007                 var all = [];
15008                 var values = v.split("^");
15009                 for(var i = 0, len = values.length; i < len; i++){
15010                     all.push(this.decodeValue(values[i]));
15011                 }
15012                 return all;
15013            case "o":
15014                 var all = {};
15015                 var values = v.split("^");
15016                 for(var i = 0, len = values.length; i < len; i++){
15017                     var kv = values[i].split("=");
15018                     all[kv[0]] = this.decodeValue(kv[1]);
15019                 }
15020                 return all;
15021            default:
15022                 return v;
15023         }
15024     },
15025     
15026     /**
15027      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15028      * @param {Mixed} value The value to encode
15029      * @return {String} The encoded value
15030      */
15031     encodeValue : function(v){
15032         var enc;
15033         if(typeof v == "number"){
15034             enc = "n:" + v;
15035         }else if(typeof v == "boolean"){
15036             enc = "b:" + (v ? "1" : "0");
15037         }else if(v instanceof Date){
15038             enc = "d:" + v.toGMTString();
15039         }else if(v instanceof Array){
15040             var flat = "";
15041             for(var i = 0, len = v.length; i < len; i++){
15042                 flat += this.encodeValue(v[i]);
15043                 if(i != len-1) {
15044                     flat += "^";
15045                 }
15046             }
15047             enc = "a:" + flat;
15048         }else if(typeof v == "object"){
15049             var flat = "";
15050             for(var key in v){
15051                 if(typeof v[key] != "function"){
15052                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15053                 }
15054             }
15055             enc = "o:" + flat.substring(0, flat.length-1);
15056         }else{
15057             enc = "s:" + v;
15058         }
15059         return escape(enc);        
15060     }
15061 });
15062
15063 /*
15064  * Based on:
15065  * Ext JS Library 1.1.1
15066  * Copyright(c) 2006-2007, Ext JS, LLC.
15067  *
15068  * Originally Released Under LGPL - original licence link has changed is not relivant.
15069  *
15070  * Fork - LGPL
15071  * <script type="text/javascript">
15072  */
15073 /**
15074  * @class Roo.state.Manager
15075  * This is the global state manager. By default all components that are "state aware" check this class
15076  * for state information if you don't pass them a custom state provider. In order for this class
15077  * to be useful, it must be initialized with a provider when your application initializes.
15078  <pre><code>
15079 // in your initialization function
15080 init : function(){
15081    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15082    ...
15083    // supposed you have a {@link Roo.BorderLayout}
15084    var layout = new Roo.BorderLayout(...);
15085    layout.restoreState();
15086    // or a {Roo.BasicDialog}
15087    var dialog = new Roo.BasicDialog(...);
15088    dialog.restoreState();
15089  </code></pre>
15090  * @singleton
15091  */
15092 Roo.state.Manager = function(){
15093     var provider = new Roo.state.Provider();
15094     
15095     return {
15096         /**
15097          * Configures the default state provider for your application
15098          * @param {Provider} stateProvider The state provider to set
15099          */
15100         setProvider : function(stateProvider){
15101             provider = stateProvider;
15102         },
15103         
15104         /**
15105          * Returns the current value for a key
15106          * @param {String} name The key name
15107          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15108          * @return {Mixed} The state data
15109          */
15110         get : function(key, defaultValue){
15111             return provider.get(key, defaultValue);
15112         },
15113         
15114         /**
15115          * Sets the value for a key
15116          * @param {String} name The key name
15117          * @param {Mixed} value The state data
15118          */
15119          set : function(key, value){
15120             provider.set(key, value);
15121         },
15122         
15123         /**
15124          * Clears a value from the state
15125          * @param {String} name The key name
15126          */
15127         clear : function(key){
15128             provider.clear(key);
15129         },
15130         
15131         /**
15132          * Gets the currently configured state provider
15133          * @return {Provider} The state provider
15134          */
15135         getProvider : function(){
15136             return provider;
15137         }
15138     };
15139 }();
15140 /*
15141  * Based on:
15142  * Ext JS Library 1.1.1
15143  * Copyright(c) 2006-2007, Ext JS, LLC.
15144  *
15145  * Originally Released Under LGPL - original licence link has changed is not relivant.
15146  *
15147  * Fork - LGPL
15148  * <script type="text/javascript">
15149  */
15150 /**
15151  * @class Roo.state.CookieProvider
15152  * @extends Roo.state.Provider
15153  * The default Provider implementation which saves state via cookies.
15154  * <br />Usage:
15155  <pre><code>
15156    var cp = new Roo.state.CookieProvider({
15157        path: "/cgi-bin/",
15158        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15159        domain: "roojs.com"
15160    })
15161    Roo.state.Manager.setProvider(cp);
15162  </code></pre>
15163  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15164  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15165  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15166  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15167  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15168  * domain the page is running on including the 'www' like 'www.roojs.com')
15169  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15170  * @constructor
15171  * Create a new CookieProvider
15172  * @param {Object} config The configuration object
15173  */
15174 Roo.state.CookieProvider = function(config){
15175     Roo.state.CookieProvider.superclass.constructor.call(this);
15176     this.path = "/";
15177     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15178     this.domain = null;
15179     this.secure = false;
15180     Roo.apply(this, config);
15181     this.state = this.readCookies();
15182 };
15183
15184 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15185     // private
15186     set : function(name, value){
15187         if(typeof value == "undefined" || value === null){
15188             this.clear(name);
15189             return;
15190         }
15191         this.setCookie(name, value);
15192         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15193     },
15194
15195     // private
15196     clear : function(name){
15197         this.clearCookie(name);
15198         Roo.state.CookieProvider.superclass.clear.call(this, name);
15199     },
15200
15201     // private
15202     readCookies : function(){
15203         var cookies = {};
15204         var c = document.cookie + ";";
15205         var re = /\s?(.*?)=(.*?);/g;
15206         var matches;
15207         while((matches = re.exec(c)) != null){
15208             var name = matches[1];
15209             var value = matches[2];
15210             if(name && name.substring(0,3) == "ys-"){
15211                 cookies[name.substr(3)] = this.decodeValue(value);
15212             }
15213         }
15214         return cookies;
15215     },
15216
15217     // private
15218     setCookie : function(name, value){
15219         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15220            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15221            ((this.path == null) ? "" : ("; path=" + this.path)) +
15222            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15223            ((this.secure == true) ? "; secure" : "");
15224     },
15225
15226     // private
15227     clearCookie : function(name){
15228         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15229            ((this.path == null) ? "" : ("; path=" + this.path)) +
15230            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15231            ((this.secure == true) ? "; secure" : "");
15232     }
15233 });/*
15234  * Based on:
15235  * Ext JS Library 1.1.1
15236  * Copyright(c) 2006-2007, Ext JS, LLC.
15237  *
15238  * Originally Released Under LGPL - original licence link has changed is not relivant.
15239  *
15240  * Fork - LGPL
15241  * <script type="text/javascript">
15242  */
15243  
15244
15245 /**
15246  * @class Roo.ComponentMgr
15247  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15248  * @singleton
15249  */
15250 Roo.ComponentMgr = function(){
15251     var all = new Roo.util.MixedCollection();
15252
15253     return {
15254         /**
15255          * Registers a component.
15256          * @param {Roo.Component} c The component
15257          */
15258         register : function(c){
15259             all.add(c);
15260         },
15261
15262         /**
15263          * Unregisters a component.
15264          * @param {Roo.Component} c The component
15265          */
15266         unregister : function(c){
15267             all.remove(c);
15268         },
15269
15270         /**
15271          * Returns a component by id
15272          * @param {String} id The component id
15273          */
15274         get : function(id){
15275             return all.get(id);
15276         },
15277
15278         /**
15279          * Registers a function that will be called when a specified component is added to ComponentMgr
15280          * @param {String} id The component id
15281          * @param {Funtction} fn The callback function
15282          * @param {Object} scope The scope of the callback
15283          */
15284         onAvailable : function(id, fn, scope){
15285             all.on("add", function(index, o){
15286                 if(o.id == id){
15287                     fn.call(scope || o, o);
15288                     all.un("add", fn, scope);
15289                 }
15290             });
15291         }
15292     };
15293 }();/*
15294  * Based on:
15295  * Ext JS Library 1.1.1
15296  * Copyright(c) 2006-2007, Ext JS, LLC.
15297  *
15298  * Originally Released Under LGPL - original licence link has changed is not relivant.
15299  *
15300  * Fork - LGPL
15301  * <script type="text/javascript">
15302  */
15303  
15304 /**
15305  * @class Roo.Component
15306  * @extends Roo.util.Observable
15307  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15308  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15309  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15310  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15311  * All visual components (widgets) that require rendering into a layout should subclass Component.
15312  * @constructor
15313  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15314  * 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
15315  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15316  */
15317 Roo.Component = function(config){
15318     config = config || {};
15319     if(config.tagName || config.dom || typeof config == "string"){ // element object
15320         config = {el: config, id: config.id || config};
15321     }
15322     this.initialConfig = config;
15323
15324     Roo.apply(this, config);
15325     this.addEvents({
15326         /**
15327          * @event disable
15328          * Fires after the component is disabled.
15329              * @param {Roo.Component} this
15330              */
15331         disable : true,
15332         /**
15333          * @event enable
15334          * Fires after the component is enabled.
15335              * @param {Roo.Component} this
15336              */
15337         enable : true,
15338         /**
15339          * @event beforeshow
15340          * Fires before the component is shown.  Return false to stop the show.
15341              * @param {Roo.Component} this
15342              */
15343         beforeshow : true,
15344         /**
15345          * @event show
15346          * Fires after the component is shown.
15347              * @param {Roo.Component} this
15348              */
15349         show : true,
15350         /**
15351          * @event beforehide
15352          * Fires before the component is hidden. Return false to stop the hide.
15353              * @param {Roo.Component} this
15354              */
15355         beforehide : true,
15356         /**
15357          * @event hide
15358          * Fires after the component is hidden.
15359              * @param {Roo.Component} this
15360              */
15361         hide : true,
15362         /**
15363          * @event beforerender
15364          * Fires before the component is rendered. Return false to stop the render.
15365              * @param {Roo.Component} this
15366              */
15367         beforerender : true,
15368         /**
15369          * @event render
15370          * Fires after the component is rendered.
15371              * @param {Roo.Component} this
15372              */
15373         render : true,
15374         /**
15375          * @event beforedestroy
15376          * Fires before the component is destroyed. Return false to stop the destroy.
15377              * @param {Roo.Component} this
15378              */
15379         beforedestroy : true,
15380         /**
15381          * @event destroy
15382          * Fires after the component is destroyed.
15383              * @param {Roo.Component} this
15384              */
15385         destroy : true
15386     });
15387     if(!this.id){
15388         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15389     }
15390     Roo.ComponentMgr.register(this);
15391     Roo.Component.superclass.constructor.call(this);
15392     this.initComponent();
15393     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15394         this.render(this.renderTo);
15395         delete this.renderTo;
15396     }
15397 };
15398
15399 /** @private */
15400 Roo.Component.AUTO_ID = 1000;
15401
15402 Roo.extend(Roo.Component, Roo.util.Observable, {
15403     /**
15404      * @scope Roo.Component.prototype
15405      * @type {Boolean}
15406      * true if this component is hidden. Read-only.
15407      */
15408     hidden : false,
15409     /**
15410      * @type {Boolean}
15411      * true if this component is disabled. Read-only.
15412      */
15413     disabled : false,
15414     /**
15415      * @type {Boolean}
15416      * true if this component has been rendered. Read-only.
15417      */
15418     rendered : false,
15419     
15420     /** @cfg {String} disableClass
15421      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15422      */
15423     disabledClass : "x-item-disabled",
15424         /** @cfg {Boolean} allowDomMove
15425          * Whether the component can move the Dom node when rendering (defaults to true).
15426          */
15427     allowDomMove : true,
15428     /** @cfg {String} hideMode (display|visibility)
15429      * How this component should hidden. Supported values are
15430      * "visibility" (css visibility), "offsets" (negative offset position) and
15431      * "display" (css display) - defaults to "display".
15432      */
15433     hideMode: 'display',
15434
15435     /** @private */
15436     ctype : "Roo.Component",
15437
15438     /**
15439      * @cfg {String} actionMode 
15440      * which property holds the element that used for  hide() / show() / disable() / enable()
15441      * default is 'el' 
15442      */
15443     actionMode : "el",
15444
15445     /** @private */
15446     getActionEl : function(){
15447         return this[this.actionMode];
15448     },
15449
15450     initComponent : Roo.emptyFn,
15451     /**
15452      * If this is a lazy rendering component, render it to its container element.
15453      * @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.
15454      */
15455     render : function(container, position){
15456         
15457         if(this.rendered){
15458             return this;
15459         }
15460         
15461         if(this.fireEvent("beforerender", this) === false){
15462             return false;
15463         }
15464         
15465         if(!container && this.el){
15466             this.el = Roo.get(this.el);
15467             container = this.el.dom.parentNode;
15468             this.allowDomMove = false;
15469         }
15470         this.container = Roo.get(container);
15471         this.rendered = true;
15472         if(position !== undefined){
15473             if(typeof position == 'number'){
15474                 position = this.container.dom.childNodes[position];
15475             }else{
15476                 position = Roo.getDom(position);
15477             }
15478         }
15479         this.onRender(this.container, position || null);
15480         if(this.cls){
15481             this.el.addClass(this.cls);
15482             delete this.cls;
15483         }
15484         if(this.style){
15485             this.el.applyStyles(this.style);
15486             delete this.style;
15487         }
15488         this.fireEvent("render", this);
15489         this.afterRender(this.container);
15490         if(this.hidden){
15491             this.hide();
15492         }
15493         if(this.disabled){
15494             this.disable();
15495         }
15496
15497         return this;
15498         
15499     },
15500
15501     /** @private */
15502     // default function is not really useful
15503     onRender : function(ct, position){
15504         if(this.el){
15505             this.el = Roo.get(this.el);
15506             if(this.allowDomMove !== false){
15507                 ct.dom.insertBefore(this.el.dom, position);
15508             }
15509         }
15510     },
15511
15512     /** @private */
15513     getAutoCreate : function(){
15514         var cfg = typeof this.autoCreate == "object" ?
15515                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15516         if(this.id && !cfg.id){
15517             cfg.id = this.id;
15518         }
15519         return cfg;
15520     },
15521
15522     /** @private */
15523     afterRender : Roo.emptyFn,
15524
15525     /**
15526      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15527      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15528      */
15529     destroy : function(){
15530         if(this.fireEvent("beforedestroy", this) !== false){
15531             this.purgeListeners();
15532             this.beforeDestroy();
15533             if(this.rendered){
15534                 this.el.removeAllListeners();
15535                 this.el.remove();
15536                 if(this.actionMode == "container"){
15537                     this.container.remove();
15538                 }
15539             }
15540             this.onDestroy();
15541             Roo.ComponentMgr.unregister(this);
15542             this.fireEvent("destroy", this);
15543         }
15544     },
15545
15546         /** @private */
15547     beforeDestroy : function(){
15548
15549     },
15550
15551         /** @private */
15552         onDestroy : function(){
15553
15554     },
15555
15556     /**
15557      * Returns the underlying {@link Roo.Element}.
15558      * @return {Roo.Element} The element
15559      */
15560     getEl : function(){
15561         return this.el;
15562     },
15563
15564     /**
15565      * Returns the id of this component.
15566      * @return {String}
15567      */
15568     getId : function(){
15569         return this.id;
15570     },
15571
15572     /**
15573      * Try to focus this component.
15574      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15575      * @return {Roo.Component} this
15576      */
15577     focus : function(selectText){
15578         if(this.rendered){
15579             this.el.focus();
15580             if(selectText === true){
15581                 this.el.dom.select();
15582             }
15583         }
15584         return this;
15585     },
15586
15587     /** @private */
15588     blur : function(){
15589         if(this.rendered){
15590             this.el.blur();
15591         }
15592         return this;
15593     },
15594
15595     /**
15596      * Disable this component.
15597      * @return {Roo.Component} this
15598      */
15599     disable : function(){
15600         if(this.rendered){
15601             this.onDisable();
15602         }
15603         this.disabled = true;
15604         this.fireEvent("disable", this);
15605         return this;
15606     },
15607
15608         // private
15609     onDisable : function(){
15610         this.getActionEl().addClass(this.disabledClass);
15611         this.el.dom.disabled = true;
15612     },
15613
15614     /**
15615      * Enable this component.
15616      * @return {Roo.Component} this
15617      */
15618     enable : function(){
15619         if(this.rendered){
15620             this.onEnable();
15621         }
15622         this.disabled = false;
15623         this.fireEvent("enable", this);
15624         return this;
15625     },
15626
15627         // private
15628     onEnable : function(){
15629         this.getActionEl().removeClass(this.disabledClass);
15630         this.el.dom.disabled = false;
15631     },
15632
15633     /**
15634      * Convenience function for setting disabled/enabled by boolean.
15635      * @param {Boolean} disabled
15636      */
15637     setDisabled : function(disabled){
15638         this[disabled ? "disable" : "enable"]();
15639     },
15640
15641     /**
15642      * Show this component.
15643      * @return {Roo.Component} this
15644      */
15645     show: function(){
15646         if(this.fireEvent("beforeshow", this) !== false){
15647             this.hidden = false;
15648             if(this.rendered){
15649                 this.onShow();
15650             }
15651             this.fireEvent("show", this);
15652         }
15653         return this;
15654     },
15655
15656     // private
15657     onShow : function(){
15658         var ae = this.getActionEl();
15659         if(this.hideMode == 'visibility'){
15660             ae.dom.style.visibility = "visible";
15661         }else if(this.hideMode == 'offsets'){
15662             ae.removeClass('x-hidden');
15663         }else{
15664             ae.dom.style.display = "";
15665         }
15666     },
15667
15668     /**
15669      * Hide this component.
15670      * @return {Roo.Component} this
15671      */
15672     hide: function(){
15673         if(this.fireEvent("beforehide", this) !== false){
15674             this.hidden = true;
15675             if(this.rendered){
15676                 this.onHide();
15677             }
15678             this.fireEvent("hide", this);
15679         }
15680         return this;
15681     },
15682
15683     // private
15684     onHide : function(){
15685         var ae = this.getActionEl();
15686         if(this.hideMode == 'visibility'){
15687             ae.dom.style.visibility = "hidden";
15688         }else if(this.hideMode == 'offsets'){
15689             ae.addClass('x-hidden');
15690         }else{
15691             ae.dom.style.display = "none";
15692         }
15693     },
15694
15695     /**
15696      * Convenience function to hide or show this component by boolean.
15697      * @param {Boolean} visible True to show, false to hide
15698      * @return {Roo.Component} this
15699      */
15700     setVisible: function(visible){
15701         if(visible) {
15702             this.show();
15703         }else{
15704             this.hide();
15705         }
15706         return this;
15707     },
15708
15709     /**
15710      * Returns true if this component is visible.
15711      */
15712     isVisible : function(){
15713         return this.getActionEl().isVisible();
15714     },
15715
15716     cloneConfig : function(overrides){
15717         overrides = overrides || {};
15718         var id = overrides.id || Roo.id();
15719         var cfg = Roo.applyIf(overrides, this.initialConfig);
15720         cfg.id = id; // prevent dup id
15721         return new this.constructor(cfg);
15722     }
15723 });/*
15724  * Based on:
15725  * Ext JS Library 1.1.1
15726  * Copyright(c) 2006-2007, Ext JS, LLC.
15727  *
15728  * Originally Released Under LGPL - original licence link has changed is not relivant.
15729  *
15730  * Fork - LGPL
15731  * <script type="text/javascript">
15732  */
15733
15734 /**
15735  * @class Roo.BoxComponent
15736  * @extends Roo.Component
15737  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15738  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15739  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15740  * layout containers.
15741  * @constructor
15742  * @param {Roo.Element/String/Object} config The configuration options.
15743  */
15744 Roo.BoxComponent = function(config){
15745     Roo.Component.call(this, config);
15746     this.addEvents({
15747         /**
15748          * @event resize
15749          * Fires after the component is resized.
15750              * @param {Roo.Component} this
15751              * @param {Number} adjWidth The box-adjusted width that was set
15752              * @param {Number} adjHeight The box-adjusted height that was set
15753              * @param {Number} rawWidth The width that was originally specified
15754              * @param {Number} rawHeight The height that was originally specified
15755              */
15756         resize : true,
15757         /**
15758          * @event move
15759          * Fires after the component is moved.
15760              * @param {Roo.Component} this
15761              * @param {Number} x The new x position
15762              * @param {Number} y The new y position
15763              */
15764         move : true
15765     });
15766 };
15767
15768 Roo.extend(Roo.BoxComponent, Roo.Component, {
15769     // private, set in afterRender to signify that the component has been rendered
15770     boxReady : false,
15771     // private, used to defer height settings to subclasses
15772     deferHeight: false,
15773     /** @cfg {Number} width
15774      * width (optional) size of component
15775      */
15776      /** @cfg {Number} height
15777      * height (optional) size of component
15778      */
15779      
15780     /**
15781      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15782      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15783      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15784      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15785      * @return {Roo.BoxComponent} this
15786      */
15787     setSize : function(w, h){
15788         // support for standard size objects
15789         if(typeof w == 'object'){
15790             h = w.height;
15791             w = w.width;
15792         }
15793         // not rendered
15794         if(!this.boxReady){
15795             this.width = w;
15796             this.height = h;
15797             return this;
15798         }
15799
15800         // prevent recalcs when not needed
15801         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15802             return this;
15803         }
15804         this.lastSize = {width: w, height: h};
15805
15806         var adj = this.adjustSize(w, h);
15807         var aw = adj.width, ah = adj.height;
15808         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15809             var rz = this.getResizeEl();
15810             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15811                 rz.setSize(aw, ah);
15812             }else if(!this.deferHeight && ah !== undefined){
15813                 rz.setHeight(ah);
15814             }else if(aw !== undefined){
15815                 rz.setWidth(aw);
15816             }
15817             this.onResize(aw, ah, w, h);
15818             this.fireEvent('resize', this, aw, ah, w, h);
15819         }
15820         return this;
15821     },
15822
15823     /**
15824      * Gets the current size of the component's underlying element.
15825      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15826      */
15827     getSize : function(){
15828         return this.el.getSize();
15829     },
15830
15831     /**
15832      * Gets the current XY position of the component's underlying element.
15833      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15834      * @return {Array} The XY position of the element (e.g., [100, 200])
15835      */
15836     getPosition : function(local){
15837         if(local === true){
15838             return [this.el.getLeft(true), this.el.getTop(true)];
15839         }
15840         return this.xy || this.el.getXY();
15841     },
15842
15843     /**
15844      * Gets the current box measurements of the component's underlying element.
15845      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15846      * @returns {Object} box An object in the format {x, y, width, height}
15847      */
15848     getBox : function(local){
15849         var s = this.el.getSize();
15850         if(local){
15851             s.x = this.el.getLeft(true);
15852             s.y = this.el.getTop(true);
15853         }else{
15854             var xy = this.xy || this.el.getXY();
15855             s.x = xy[0];
15856             s.y = xy[1];
15857         }
15858         return s;
15859     },
15860
15861     /**
15862      * Sets the current box measurements of the component's underlying element.
15863      * @param {Object} box An object in the format {x, y, width, height}
15864      * @returns {Roo.BoxComponent} this
15865      */
15866     updateBox : function(box){
15867         this.setSize(box.width, box.height);
15868         this.setPagePosition(box.x, box.y);
15869         return this;
15870     },
15871
15872     // protected
15873     getResizeEl : function(){
15874         return this.resizeEl || this.el;
15875     },
15876
15877     // protected
15878     getPositionEl : function(){
15879         return this.positionEl || this.el;
15880     },
15881
15882     /**
15883      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15884      * This method fires the move event.
15885      * @param {Number} left The new left
15886      * @param {Number} top The new top
15887      * @returns {Roo.BoxComponent} this
15888      */
15889     setPosition : function(x, y){
15890         this.x = x;
15891         this.y = y;
15892         if(!this.boxReady){
15893             return this;
15894         }
15895         var adj = this.adjustPosition(x, y);
15896         var ax = adj.x, ay = adj.y;
15897
15898         var el = this.getPositionEl();
15899         if(ax !== undefined || ay !== undefined){
15900             if(ax !== undefined && ay !== undefined){
15901                 el.setLeftTop(ax, ay);
15902             }else if(ax !== undefined){
15903                 el.setLeft(ax);
15904             }else if(ay !== undefined){
15905                 el.setTop(ay);
15906             }
15907             this.onPosition(ax, ay);
15908             this.fireEvent('move', this, ax, ay);
15909         }
15910         return this;
15911     },
15912
15913     /**
15914      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15915      * This method fires the move event.
15916      * @param {Number} x The new x position
15917      * @param {Number} y The new y position
15918      * @returns {Roo.BoxComponent} this
15919      */
15920     setPagePosition : function(x, y){
15921         this.pageX = x;
15922         this.pageY = y;
15923         if(!this.boxReady){
15924             return;
15925         }
15926         if(x === undefined || y === undefined){ // cannot translate undefined points
15927             return;
15928         }
15929         var p = this.el.translatePoints(x, y);
15930         this.setPosition(p.left, p.top);
15931         return this;
15932     },
15933
15934     // private
15935     onRender : function(ct, position){
15936         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15937         if(this.resizeEl){
15938             this.resizeEl = Roo.get(this.resizeEl);
15939         }
15940         if(this.positionEl){
15941             this.positionEl = Roo.get(this.positionEl);
15942         }
15943     },
15944
15945     // private
15946     afterRender : function(){
15947         Roo.BoxComponent.superclass.afterRender.call(this);
15948         this.boxReady = true;
15949         this.setSize(this.width, this.height);
15950         if(this.x || this.y){
15951             this.setPosition(this.x, this.y);
15952         }
15953         if(this.pageX || this.pageY){
15954             this.setPagePosition(this.pageX, this.pageY);
15955         }
15956     },
15957
15958     /**
15959      * Force the component's size to recalculate based on the underlying element's current height and width.
15960      * @returns {Roo.BoxComponent} this
15961      */
15962     syncSize : function(){
15963         delete this.lastSize;
15964         this.setSize(this.el.getWidth(), this.el.getHeight());
15965         return this;
15966     },
15967
15968     /**
15969      * Called after the component is resized, this method is empty by default but can be implemented by any
15970      * subclass that needs to perform custom logic after a resize occurs.
15971      * @param {Number} adjWidth The box-adjusted width that was set
15972      * @param {Number} adjHeight The box-adjusted height that was set
15973      * @param {Number} rawWidth The width that was originally specified
15974      * @param {Number} rawHeight The height that was originally specified
15975      */
15976     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15977
15978     },
15979
15980     /**
15981      * Called after the component is moved, this method is empty by default but can be implemented by any
15982      * subclass that needs to perform custom logic after a move occurs.
15983      * @param {Number} x The new x position
15984      * @param {Number} y The new y position
15985      */
15986     onPosition : function(x, y){
15987
15988     },
15989
15990     // private
15991     adjustSize : function(w, h){
15992         if(this.autoWidth){
15993             w = 'auto';
15994         }
15995         if(this.autoHeight){
15996             h = 'auto';
15997         }
15998         return {width : w, height: h};
15999     },
16000
16001     // private
16002     adjustPosition : function(x, y){
16003         return {x : x, y: y};
16004     }
16005 });/*
16006  * Original code for Roojs - LGPL
16007  * <script type="text/javascript">
16008  */
16009  
16010 /**
16011  * @class Roo.XComponent
16012  * A delayed Element creator...
16013  * Or a way to group chunks of interface together.
16014  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16015  *  used in conjunction with XComponent.build() it will create an instance of each element,
16016  *  then call addxtype() to build the User interface.
16017  * 
16018  * Mypart.xyx = new Roo.XComponent({
16019
16020     parent : 'Mypart.xyz', // empty == document.element.!!
16021     order : '001',
16022     name : 'xxxx'
16023     region : 'xxxx'
16024     disabled : function() {} 
16025      
16026     tree : function() { // return an tree of xtype declared components
16027         var MODULE = this;
16028         return 
16029         {
16030             xtype : 'NestedLayoutPanel',
16031             // technicall
16032         }
16033      ]
16034  *})
16035  *
16036  *
16037  * It can be used to build a big heiracy, with parent etc.
16038  * or you can just use this to render a single compoent to a dom element
16039  * MYPART.render(Roo.Element | String(id) | dom_element )
16040  *
16041  *
16042  * Usage patterns.
16043  *
16044  * Classic Roo
16045  *
16046  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16047  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16048  *
16049  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16050  *
16051  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16052  * - if mulitple topModules exist, the last one is defined as the top module.
16053  *
16054  * Embeded Roo
16055  * 
16056  * When the top level or multiple modules are to embedded into a existing HTML page,
16057  * the parent element can container '#id' of the element where the module will be drawn.
16058  *
16059  * Bootstrap Roo
16060  *
16061  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16062  * it relies more on a include mechanism, where sub modules are included into an outer page.
16063  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16064  * 
16065  * Bootstrap Roo Included elements
16066  *
16067  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16068  * hence confusing the component builder as it thinks there are multiple top level elements. 
16069  *
16070  * String Over-ride & Translations
16071  *
16072  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16073  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16074  * are needed. @see Roo.XComponent.overlayString  
16075  * 
16076  * 
16077  * 
16078  * @extends Roo.util.Observable
16079  * @constructor
16080  * @param cfg {Object} configuration of component
16081  * 
16082  */
16083 Roo.XComponent = function(cfg) {
16084     Roo.apply(this, cfg);
16085     this.addEvents({ 
16086         /**
16087              * @event built
16088              * Fires when this the componnt is built
16089              * @param {Roo.XComponent} c the component
16090              */
16091         'built' : true
16092         
16093     });
16094     this.region = this.region || 'center'; // default..
16095     Roo.XComponent.register(this);
16096     this.modules = false;
16097     this.el = false; // where the layout goes..
16098     
16099     
16100 }
16101 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16102     /**
16103      * @property el
16104      * The created element (with Roo.factory())
16105      * @type {Roo.Layout}
16106      */
16107     el  : false,
16108     
16109     /**
16110      * @property el
16111      * for BC  - use el in new code
16112      * @type {Roo.Layout}
16113      */
16114     panel : false,
16115     
16116     /**
16117      * @property layout
16118      * for BC  - use el in new code
16119      * @type {Roo.Layout}
16120      */
16121     layout : false,
16122     
16123      /**
16124      * @cfg {Function|boolean} disabled
16125      * If this module is disabled by some rule, return true from the funtion
16126      */
16127     disabled : false,
16128     
16129     /**
16130      * @cfg {String} parent 
16131      * Name of parent element which it get xtype added to..
16132      */
16133     parent: false,
16134     
16135     /**
16136      * @cfg {String} order
16137      * Used to set the order in which elements are created (usefull for multiple tabs)
16138      */
16139     
16140     order : false,
16141     /**
16142      * @cfg {String} name
16143      * String to display while loading.
16144      */
16145     name : false,
16146     /**
16147      * @cfg {String} region
16148      * Region to render component to (defaults to center)
16149      */
16150     region : 'center',
16151     
16152     /**
16153      * @cfg {Array} items
16154      * A single item array - the first element is the root of the tree..
16155      * It's done this way to stay compatible with the Xtype system...
16156      */
16157     items : false,
16158     
16159     /**
16160      * @property _tree
16161      * The method that retuns the tree of parts that make up this compoennt 
16162      * @type {function}
16163      */
16164     _tree  : false,
16165     
16166      /**
16167      * render
16168      * render element to dom or tree
16169      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16170      */
16171     
16172     render : function(el)
16173     {
16174         
16175         el = el || false;
16176         var hp = this.parent ? 1 : 0;
16177         Roo.debug &&  Roo.log(this);
16178         
16179         var tree = this._tree ? this._tree() : this.tree();
16180
16181         
16182         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16183             // if parent is a '#.....' string, then let's use that..
16184             var ename = this.parent.substr(1);
16185             this.parent = false;
16186             Roo.debug && Roo.log(ename);
16187             switch (ename) {
16188                 case 'bootstrap-body':
16189                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16190                         // this is the BorderLayout standard?
16191                        this.parent = { el : true };
16192                        break;
16193                     }
16194                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16195                         // need to insert stuff...
16196                         this.parent =  {
16197                              el : new Roo.bootstrap.layout.Border({
16198                                  el : document.body, 
16199                      
16200                                  center: {
16201                                     titlebar: false,
16202                                     autoScroll:false,
16203                                     closeOnTab: true,
16204                                     tabPosition: 'top',
16205                                       //resizeTabs: true,
16206                                     alwaysShowTabs: true,
16207                                     hideTabs: false
16208                                      //minTabWidth: 140
16209                                  }
16210                              })
16211                         
16212                          };
16213                          break;
16214                     }
16215                          
16216                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16217                         this.parent = { el :  new  Roo.bootstrap.Body() };
16218                         Roo.debug && Roo.log("setting el to doc body");
16219                          
16220                     } else {
16221                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16222                     }
16223                     break;
16224                 case 'bootstrap':
16225                     this.parent = { el : true};
16226                     // fall through
16227                 default:
16228                     el = Roo.get(ename);
16229                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16230                         this.parent = { el : true};
16231                     }
16232                     
16233                     break;
16234             }
16235                 
16236             
16237             if (!el && !this.parent) {
16238                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16239                 return;
16240             }
16241         }
16242         
16243         Roo.debug && Roo.log("EL:");
16244         Roo.debug && Roo.log(el);
16245         Roo.debug && Roo.log("this.parent.el:");
16246         Roo.debug && Roo.log(this.parent.el);
16247         
16248
16249         // altertive root elements ??? - we need a better way to indicate these.
16250         var is_alt = Roo.XComponent.is_alt ||
16251                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16252                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16253                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16254         
16255         
16256         
16257         if (!this.parent && is_alt) {
16258             //el = Roo.get(document.body);
16259             this.parent = { el : true };
16260         }
16261             
16262             
16263         
16264         if (!this.parent) {
16265             
16266             Roo.debug && Roo.log("no parent - creating one");
16267             
16268             el = el ? Roo.get(el) : false;      
16269             
16270             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16271                 
16272                 this.parent =  {
16273                     el : new Roo.bootstrap.layout.Border({
16274                         el: el || document.body,
16275                     
16276                         center: {
16277                             titlebar: false,
16278                             autoScroll:false,
16279                             closeOnTab: true,
16280                             tabPosition: 'top',
16281                              //resizeTabs: true,
16282                             alwaysShowTabs: false,
16283                             hideTabs: true,
16284                             minTabWidth: 140,
16285                             overflow: 'visible'
16286                          }
16287                      })
16288                 };
16289             } else {
16290             
16291                 // it's a top level one..
16292                 this.parent =  {
16293                     el : new Roo.BorderLayout(el || document.body, {
16294                         center: {
16295                             titlebar: false,
16296                             autoScroll:false,
16297                             closeOnTab: true,
16298                             tabPosition: 'top',
16299                              //resizeTabs: true,
16300                             alwaysShowTabs: el && hp? false :  true,
16301                             hideTabs: el || !hp ? true :  false,
16302                             minTabWidth: 140
16303                          }
16304                     })
16305                 };
16306             }
16307         }
16308         
16309         if (!this.parent.el) {
16310                 // probably an old style ctor, which has been disabled.
16311                 return;
16312
16313         }
16314                 // The 'tree' method is  '_tree now' 
16315             
16316         tree.region = tree.region || this.region;
16317         var is_body = false;
16318         if (this.parent.el === true) {
16319             // bootstrap... - body..
16320             if (el) {
16321                 tree.el = el;
16322             }
16323             this.parent.el = Roo.factory(tree);
16324             is_body = true;
16325         }
16326         
16327         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16328         this.fireEvent('built', this);
16329         
16330         this.panel = this.el;
16331         this.layout = this.panel.layout;
16332         this.parentLayout = this.parent.layout  || false;  
16333          
16334     }
16335     
16336 });
16337
16338 Roo.apply(Roo.XComponent, {
16339     /**
16340      * @property  hideProgress
16341      * true to disable the building progress bar.. usefull on single page renders.
16342      * @type Boolean
16343      */
16344     hideProgress : false,
16345     /**
16346      * @property  buildCompleted
16347      * True when the builder has completed building the interface.
16348      * @type Boolean
16349      */
16350     buildCompleted : false,
16351      
16352     /**
16353      * @property  topModule
16354      * the upper most module - uses document.element as it's constructor.
16355      * @type Object
16356      */
16357      
16358     topModule  : false,
16359       
16360     /**
16361      * @property  modules
16362      * array of modules to be created by registration system.
16363      * @type {Array} of Roo.XComponent
16364      */
16365     
16366     modules : [],
16367     /**
16368      * @property  elmodules
16369      * array of modules to be created by which use #ID 
16370      * @type {Array} of Roo.XComponent
16371      */
16372      
16373     elmodules : [],
16374
16375      /**
16376      * @property  is_alt
16377      * Is an alternative Root - normally used by bootstrap or other systems,
16378      *    where the top element in the tree can wrap 'body' 
16379      * @type {boolean}  (default false)
16380      */
16381      
16382     is_alt : false,
16383     /**
16384      * @property  build_from_html
16385      * Build elements from html - used by bootstrap HTML stuff 
16386      *    - this is cleared after build is completed
16387      * @type {boolean}    (default false)
16388      */
16389      
16390     build_from_html : false,
16391     /**
16392      * Register components to be built later.
16393      *
16394      * This solves the following issues
16395      * - Building is not done on page load, but after an authentication process has occured.
16396      * - Interface elements are registered on page load
16397      * - Parent Interface elements may not be loaded before child, so this handles that..
16398      * 
16399      *
16400      * example:
16401      * 
16402      * MyApp.register({
16403           order : '000001',
16404           module : 'Pman.Tab.projectMgr',
16405           region : 'center',
16406           parent : 'Pman.layout',
16407           disabled : false,  // or use a function..
16408         })
16409      
16410      * * @param {Object} details about module
16411      */
16412     register : function(obj) {
16413                 
16414         Roo.XComponent.event.fireEvent('register', obj);
16415         switch(typeof(obj.disabled) ) {
16416                 
16417             case 'undefined':
16418                 break;
16419             
16420             case 'function':
16421                 if ( obj.disabled() ) {
16422                         return;
16423                 }
16424                 break;
16425             
16426             default:
16427                 if (obj.disabled || obj.region == '#disabled') {
16428                         return;
16429                 }
16430                 break;
16431         }
16432                 
16433         this.modules.push(obj);
16434          
16435     },
16436     /**
16437      * convert a string to an object..
16438      * eg. 'AAA.BBB' -> finds AAA.BBB
16439
16440      */
16441     
16442     toObject : function(str)
16443     {
16444         if (!str || typeof(str) == 'object') {
16445             return str;
16446         }
16447         if (str.substring(0,1) == '#') {
16448             return str;
16449         }
16450
16451         var ar = str.split('.');
16452         var rt, o;
16453         rt = ar.shift();
16454             /** eval:var:o */
16455         try {
16456             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16457         } catch (e) {
16458             throw "Module not found : " + str;
16459         }
16460         
16461         if (o === false) {
16462             throw "Module not found : " + str;
16463         }
16464         Roo.each(ar, function(e) {
16465             if (typeof(o[e]) == 'undefined') {
16466                 throw "Module not found : " + str;
16467             }
16468             o = o[e];
16469         });
16470         
16471         return o;
16472         
16473     },
16474     
16475     
16476     /**
16477      * move modules into their correct place in the tree..
16478      * 
16479      */
16480     preBuild : function ()
16481     {
16482         var _t = this;
16483         Roo.each(this.modules , function (obj)
16484         {
16485             Roo.XComponent.event.fireEvent('beforebuild', obj);
16486             
16487             var opar = obj.parent;
16488             try { 
16489                 obj.parent = this.toObject(opar);
16490             } catch(e) {
16491                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16492                 return;
16493             }
16494             
16495             if (!obj.parent) {
16496                 Roo.debug && Roo.log("GOT top level module");
16497                 Roo.debug && Roo.log(obj);
16498                 obj.modules = new Roo.util.MixedCollection(false, 
16499                     function(o) { return o.order + '' }
16500                 );
16501                 this.topModule = obj;
16502                 return;
16503             }
16504                         // parent is a string (usually a dom element name..)
16505             if (typeof(obj.parent) == 'string') {
16506                 this.elmodules.push(obj);
16507                 return;
16508             }
16509             if (obj.parent.constructor != Roo.XComponent) {
16510                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16511             }
16512             if (!obj.parent.modules) {
16513                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16514                     function(o) { return o.order + '' }
16515                 );
16516             }
16517             if (obj.parent.disabled) {
16518                 obj.disabled = true;
16519             }
16520             obj.parent.modules.add(obj);
16521         }, this);
16522     },
16523     
16524      /**
16525      * make a list of modules to build.
16526      * @return {Array} list of modules. 
16527      */ 
16528     
16529     buildOrder : function()
16530     {
16531         var _this = this;
16532         var cmp = function(a,b) {   
16533             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16534         };
16535         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16536             throw "No top level modules to build";
16537         }
16538         
16539         // make a flat list in order of modules to build.
16540         var mods = this.topModule ? [ this.topModule ] : [];
16541                 
16542         
16543         // elmodules (is a list of DOM based modules )
16544         Roo.each(this.elmodules, function(e) {
16545             mods.push(e);
16546             if (!this.topModule &&
16547                 typeof(e.parent) == 'string' &&
16548                 e.parent.substring(0,1) == '#' &&
16549                 Roo.get(e.parent.substr(1))
16550                ) {
16551                 
16552                 _this.topModule = e;
16553             }
16554             
16555         });
16556
16557         
16558         // add modules to their parents..
16559         var addMod = function(m) {
16560             Roo.debug && Roo.log("build Order: add: " + m.name);
16561                 
16562             mods.push(m);
16563             if (m.modules && !m.disabled) {
16564                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16565                 m.modules.keySort('ASC',  cmp );
16566                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16567     
16568                 m.modules.each(addMod);
16569             } else {
16570                 Roo.debug && Roo.log("build Order: no child modules");
16571             }
16572             // not sure if this is used any more..
16573             if (m.finalize) {
16574                 m.finalize.name = m.name + " (clean up) ";
16575                 mods.push(m.finalize);
16576             }
16577             
16578         }
16579         if (this.topModule && this.topModule.modules) { 
16580             this.topModule.modules.keySort('ASC',  cmp );
16581             this.topModule.modules.each(addMod);
16582         } 
16583         return mods;
16584     },
16585     
16586      /**
16587      * Build the registered modules.
16588      * @param {Object} parent element.
16589      * @param {Function} optional method to call after module has been added.
16590      * 
16591      */ 
16592    
16593     build : function(opts) 
16594     {
16595         
16596         if (typeof(opts) != 'undefined') {
16597             Roo.apply(this,opts);
16598         }
16599         
16600         this.preBuild();
16601         var mods = this.buildOrder();
16602       
16603         //this.allmods = mods;
16604         //Roo.debug && Roo.log(mods);
16605         //return;
16606         if (!mods.length) { // should not happen
16607             throw "NO modules!!!";
16608         }
16609         
16610         
16611         var msg = "Building Interface...";
16612         // flash it up as modal - so we store the mask!?
16613         if (!this.hideProgress && Roo.MessageBox) {
16614             Roo.MessageBox.show({ title: 'loading' });
16615             Roo.MessageBox.show({
16616                title: "Please wait...",
16617                msg: msg,
16618                width:450,
16619                progress:true,
16620                buttons : false,
16621                closable:false,
16622                modal: false
16623               
16624             });
16625         }
16626         var total = mods.length;
16627         
16628         var _this = this;
16629         var progressRun = function() {
16630             if (!mods.length) {
16631                 Roo.debug && Roo.log('hide?');
16632                 if (!this.hideProgress && Roo.MessageBox) {
16633                     Roo.MessageBox.hide();
16634                 }
16635                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16636                 
16637                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16638                 
16639                 // THE END...
16640                 return false;   
16641             }
16642             
16643             var m = mods.shift();
16644             
16645             
16646             Roo.debug && Roo.log(m);
16647             // not sure if this is supported any more.. - modules that are are just function
16648             if (typeof(m) == 'function') { 
16649                 m.call(this);
16650                 return progressRun.defer(10, _this);
16651             } 
16652             
16653             
16654             msg = "Building Interface " + (total  - mods.length) + 
16655                     " of " + total + 
16656                     (m.name ? (' - ' + m.name) : '');
16657                         Roo.debug && Roo.log(msg);
16658             if (!_this.hideProgress &&  Roo.MessageBox) { 
16659                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16660             }
16661             
16662          
16663             // is the module disabled?
16664             var disabled = (typeof(m.disabled) == 'function') ?
16665                 m.disabled.call(m.module.disabled) : m.disabled;    
16666             
16667             
16668             if (disabled) {
16669                 return progressRun(); // we do not update the display!
16670             }
16671             
16672             // now build 
16673             
16674                         
16675                         
16676             m.render();
16677             // it's 10 on top level, and 1 on others??? why...
16678             return progressRun.defer(10, _this);
16679              
16680         }
16681         progressRun.defer(1, _this);
16682      
16683         
16684         
16685     },
16686     /**
16687      * Overlay a set of modified strings onto a component
16688      * This is dependant on our builder exporting the strings and 'named strings' elements.
16689      * 
16690      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
16691      * @param {Object} associative array of 'named' string and it's new value.
16692      * 
16693      */
16694         overlayStrings : function( component, strings )
16695     {
16696         if (typeof(component['_named_strings']) == 'undefined') {
16697             throw "ERROR: component does not have _named_strings";
16698         }
16699         for ( var k in strings ) {
16700             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
16701             if (md !== false) {
16702                 component['_strings'][md] = strings[k];
16703             } else {
16704                 Roo.log('could not find named string: ' + k + ' in');
16705                 Roo.log(component);
16706             }
16707             
16708         }
16709         
16710     },
16711     
16712         
16713         /**
16714          * Event Object.
16715          *
16716          *
16717          */
16718         event: false, 
16719     /**
16720          * wrapper for event.on - aliased later..  
16721          * Typically use to register a event handler for register:
16722          *
16723          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16724          *
16725          */
16726     on : false
16727    
16728     
16729     
16730 });
16731
16732 Roo.XComponent.event = new Roo.util.Observable({
16733                 events : { 
16734                         /**
16735                          * @event register
16736                          * Fires when an Component is registered,
16737                          * set the disable property on the Component to stop registration.
16738                          * @param {Roo.XComponent} c the component being registerd.
16739                          * 
16740                          */
16741                         'register' : true,
16742             /**
16743                          * @event beforebuild
16744                          * Fires before each Component is built
16745                          * can be used to apply permissions.
16746                          * @param {Roo.XComponent} c the component being registerd.
16747                          * 
16748                          */
16749                         'beforebuild' : true,
16750                         /**
16751                          * @event buildcomplete
16752                          * Fires on the top level element when all elements have been built
16753                          * @param {Roo.XComponent} the top level component.
16754                          */
16755                         'buildcomplete' : true
16756                         
16757                 }
16758 });
16759
16760 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16761  //
16762  /**
16763  * marked - a markdown parser
16764  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16765  * https://github.com/chjj/marked
16766  */
16767
16768
16769 /**
16770  *
16771  * Roo.Markdown - is a very crude wrapper around marked..
16772  *
16773  * usage:
16774  * 
16775  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16776  * 
16777  * Note: move the sample code to the bottom of this
16778  * file before uncommenting it.
16779  *
16780  */
16781
16782 Roo.Markdown = {};
16783 Roo.Markdown.toHtml = function(text) {
16784     
16785     var c = new Roo.Markdown.marked.setOptions({
16786             renderer: new Roo.Markdown.marked.Renderer(),
16787             gfm: true,
16788             tables: true,
16789             breaks: false,
16790             pedantic: false,
16791             sanitize: false,
16792             smartLists: true,
16793             smartypants: false
16794           });
16795     // A FEW HACKS!!?
16796     
16797     text = text.replace(/\\\n/g,' ');
16798     return Roo.Markdown.marked(text);
16799 };
16800 //
16801 // converter
16802 //
16803 // Wraps all "globals" so that the only thing
16804 // exposed is makeHtml().
16805 //
16806 (function() {
16807     
16808      /**
16809          * eval:var:escape
16810          * eval:var:unescape
16811          * eval:var:replace
16812          */
16813       
16814     /**
16815      * Helpers
16816      */
16817     
16818     var escape = function (html, encode) {
16819       return html
16820         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
16821         .replace(/</g, '&lt;')
16822         .replace(/>/g, '&gt;')
16823         .replace(/"/g, '&quot;')
16824         .replace(/'/g, '&#39;');
16825     }
16826     
16827     var unescape = function (html) {
16828         // explicitly match decimal, hex, and named HTML entities 
16829       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
16830         n = n.toLowerCase();
16831         if (n === 'colon') { return ':'; }
16832         if (n.charAt(0) === '#') {
16833           return n.charAt(1) === 'x'
16834             ? String.fromCharCode(parseInt(n.substring(2), 16))
16835             : String.fromCharCode(+n.substring(1));
16836         }
16837         return '';
16838       });
16839     }
16840     
16841     var replace = function (regex, opt) {
16842       regex = regex.source;
16843       opt = opt || '';
16844       return function self(name, val) {
16845         if (!name) { return new RegExp(regex, opt); }
16846         val = val.source || val;
16847         val = val.replace(/(^|[^\[])\^/g, '$1');
16848         regex = regex.replace(name, val);
16849         return self;
16850       };
16851     }
16852     
16853     /**
16854      * Block-Level Grammar
16855      */
16856     
16857     
16858     
16859     
16860     var block = {
16861       newline: /^\n+/,
16862       code: /^( {4}[^\n]+\n*)+/,
16863       fences: noop,
16864       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16865       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16866       nptable: noop,
16867       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16868       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16869       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16870       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16871       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16872       table: noop,
16873       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16874       text: /^[^\n]+/
16875     };
16876     
16877     block.bullet = /(?:[*+-]|\d+\.)/;
16878     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16879     block.item = replace(block.item, 'gm')
16880       (/bull/g, block.bullet)
16881       ();
16882     
16883     block.list = replace(block.list)
16884       (/bull/g, block.bullet)
16885       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16886       ('def', '\\n+(?=' + block.def.source + ')')
16887       ();
16888     
16889     block.blockquote = replace(block.blockquote)
16890       ('def', block.def)
16891       ();
16892     
16893     block._tag = '(?!(?:'
16894       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16895       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16896       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16897     
16898     block.html = replace(block.html)
16899       ('comment', /<!--[\s\S]*?-->/)
16900       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16901       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16902       (/tag/g, block._tag)
16903       ();
16904     
16905     block.paragraph = replace(block.paragraph)
16906       ('hr', block.hr)
16907       ('heading', block.heading)
16908       ('lheading', block.lheading)
16909       ('blockquote', block.blockquote)
16910       ('tag', '<' + block._tag)
16911       ('def', block.def)
16912       ();
16913     
16914     /**
16915      * Normal Block Grammar
16916      */
16917     
16918     block.normal = merge({}, block);
16919     
16920     /**
16921      * GFM Block Grammar
16922      */
16923     
16924     block.gfm = merge({}, block.normal, {
16925       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16926       paragraph: /^/,
16927       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16928     });
16929     
16930     block.gfm.paragraph = replace(block.paragraph)
16931       ('(?!', '(?!'
16932         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16933         + block.list.source.replace('\\1', '\\3') + '|')
16934       ();
16935     
16936     /**
16937      * GFM + Tables Block Grammar
16938      */
16939     
16940     block.tables = merge({}, block.gfm, {
16941       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16942       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16943     });
16944     
16945     /**
16946      * Block Lexer
16947      */
16948     
16949     var Lexer = function (options) {
16950       this.tokens = [];
16951       this.tokens.links = {};
16952       this.options = options || marked.defaults;
16953       this.rules = block.normal;
16954     
16955       if (this.options.gfm) {
16956         if (this.options.tables) {
16957           this.rules = block.tables;
16958         } else {
16959           this.rules = block.gfm;
16960         }
16961       }
16962     }
16963     
16964     /**
16965      * Expose Block Rules
16966      */
16967     
16968     Lexer.rules = block;
16969     
16970     /**
16971      * Static Lex Method
16972      */
16973     
16974     Lexer.lex = function(src, options) {
16975       var lexer = new Lexer(options);
16976       return lexer.lex(src);
16977     };
16978     
16979     /**
16980      * Preprocessing
16981      */
16982     
16983     Lexer.prototype.lex = function(src) {
16984       src = src
16985         .replace(/\r\n|\r/g, '\n')
16986         .replace(/\t/g, '    ')
16987         .replace(/\u00a0/g, ' ')
16988         .replace(/\u2424/g, '\n');
16989     
16990       return this.token(src, true);
16991     };
16992     
16993     /**
16994      * Lexing
16995      */
16996     
16997     Lexer.prototype.token = function(src, top, bq) {
16998       var src = src.replace(/^ +$/gm, '')
16999         , next
17000         , loose
17001         , cap
17002         , bull
17003         , b
17004         , item
17005         , space
17006         , i
17007         , l;
17008     
17009       while (src) {
17010         // newline
17011         if (cap = this.rules.newline.exec(src)) {
17012           src = src.substring(cap[0].length);
17013           if (cap[0].length > 1) {
17014             this.tokens.push({
17015               type: 'space'
17016             });
17017           }
17018         }
17019     
17020         // code
17021         if (cap = this.rules.code.exec(src)) {
17022           src = src.substring(cap[0].length);
17023           cap = cap[0].replace(/^ {4}/gm, '');
17024           this.tokens.push({
17025             type: 'code',
17026             text: !this.options.pedantic
17027               ? cap.replace(/\n+$/, '')
17028               : cap
17029           });
17030           continue;
17031         }
17032     
17033         // fences (gfm)
17034         if (cap = this.rules.fences.exec(src)) {
17035           src = src.substring(cap[0].length);
17036           this.tokens.push({
17037             type: 'code',
17038             lang: cap[2],
17039             text: cap[3] || ''
17040           });
17041           continue;
17042         }
17043     
17044         // heading
17045         if (cap = this.rules.heading.exec(src)) {
17046           src = src.substring(cap[0].length);
17047           this.tokens.push({
17048             type: 'heading',
17049             depth: cap[1].length,
17050             text: cap[2]
17051           });
17052           continue;
17053         }
17054     
17055         // table no leading pipe (gfm)
17056         if (top && (cap = this.rules.nptable.exec(src))) {
17057           src = src.substring(cap[0].length);
17058     
17059           item = {
17060             type: 'table',
17061             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17062             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17063             cells: cap[3].replace(/\n$/, '').split('\n')
17064           };
17065     
17066           for (i = 0; i < item.align.length; i++) {
17067             if (/^ *-+: *$/.test(item.align[i])) {
17068               item.align[i] = 'right';
17069             } else if (/^ *:-+: *$/.test(item.align[i])) {
17070               item.align[i] = 'center';
17071             } else if (/^ *:-+ *$/.test(item.align[i])) {
17072               item.align[i] = 'left';
17073             } else {
17074               item.align[i] = null;
17075             }
17076           }
17077     
17078           for (i = 0; i < item.cells.length; i++) {
17079             item.cells[i] = item.cells[i].split(/ *\| */);
17080           }
17081     
17082           this.tokens.push(item);
17083     
17084           continue;
17085         }
17086     
17087         // lheading
17088         if (cap = this.rules.lheading.exec(src)) {
17089           src = src.substring(cap[0].length);
17090           this.tokens.push({
17091             type: 'heading',
17092             depth: cap[2] === '=' ? 1 : 2,
17093             text: cap[1]
17094           });
17095           continue;
17096         }
17097     
17098         // hr
17099         if (cap = this.rules.hr.exec(src)) {
17100           src = src.substring(cap[0].length);
17101           this.tokens.push({
17102             type: 'hr'
17103           });
17104           continue;
17105         }
17106     
17107         // blockquote
17108         if (cap = this.rules.blockquote.exec(src)) {
17109           src = src.substring(cap[0].length);
17110     
17111           this.tokens.push({
17112             type: 'blockquote_start'
17113           });
17114     
17115           cap = cap[0].replace(/^ *> ?/gm, '');
17116     
17117           // Pass `top` to keep the current
17118           // "toplevel" state. This is exactly
17119           // how markdown.pl works.
17120           this.token(cap, top, true);
17121     
17122           this.tokens.push({
17123             type: 'blockquote_end'
17124           });
17125     
17126           continue;
17127         }
17128     
17129         // list
17130         if (cap = this.rules.list.exec(src)) {
17131           src = src.substring(cap[0].length);
17132           bull = cap[2];
17133     
17134           this.tokens.push({
17135             type: 'list_start',
17136             ordered: bull.length > 1
17137           });
17138     
17139           // Get each top-level item.
17140           cap = cap[0].match(this.rules.item);
17141     
17142           next = false;
17143           l = cap.length;
17144           i = 0;
17145     
17146           for (; i < l; i++) {
17147             item = cap[i];
17148     
17149             // Remove the list item's bullet
17150             // so it is seen as the next token.
17151             space = item.length;
17152             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17153     
17154             // Outdent whatever the
17155             // list item contains. Hacky.
17156             if (~item.indexOf('\n ')) {
17157               space -= item.length;
17158               item = !this.options.pedantic
17159                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17160                 : item.replace(/^ {1,4}/gm, '');
17161             }
17162     
17163             // Determine whether the next list item belongs here.
17164             // Backpedal if it does not belong in this list.
17165             if (this.options.smartLists && i !== l - 1) {
17166               b = block.bullet.exec(cap[i + 1])[0];
17167               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17168                 src = cap.slice(i + 1).join('\n') + src;
17169                 i = l - 1;
17170               }
17171             }
17172     
17173             // Determine whether item is loose or not.
17174             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17175             // for discount behavior.
17176             loose = next || /\n\n(?!\s*$)/.test(item);
17177             if (i !== l - 1) {
17178               next = item.charAt(item.length - 1) === '\n';
17179               if (!loose) { loose = next; }
17180             }
17181     
17182             this.tokens.push({
17183               type: loose
17184                 ? 'loose_item_start'
17185                 : 'list_item_start'
17186             });
17187     
17188             // Recurse.
17189             this.token(item, false, bq);
17190     
17191             this.tokens.push({
17192               type: 'list_item_end'
17193             });
17194           }
17195     
17196           this.tokens.push({
17197             type: 'list_end'
17198           });
17199     
17200           continue;
17201         }
17202     
17203         // html
17204         if (cap = this.rules.html.exec(src)) {
17205           src = src.substring(cap[0].length);
17206           this.tokens.push({
17207             type: this.options.sanitize
17208               ? 'paragraph'
17209               : 'html',
17210             pre: !this.options.sanitizer
17211               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17212             text: cap[0]
17213           });
17214           continue;
17215         }
17216     
17217         // def
17218         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17219           src = src.substring(cap[0].length);
17220           this.tokens.links[cap[1].toLowerCase()] = {
17221             href: cap[2],
17222             title: cap[3]
17223           };
17224           continue;
17225         }
17226     
17227         // table (gfm)
17228         if (top && (cap = this.rules.table.exec(src))) {
17229           src = src.substring(cap[0].length);
17230     
17231           item = {
17232             type: 'table',
17233             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17234             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17235             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17236           };
17237     
17238           for (i = 0; i < item.align.length; i++) {
17239             if (/^ *-+: *$/.test(item.align[i])) {
17240               item.align[i] = 'right';
17241             } else if (/^ *:-+: *$/.test(item.align[i])) {
17242               item.align[i] = 'center';
17243             } else if (/^ *:-+ *$/.test(item.align[i])) {
17244               item.align[i] = 'left';
17245             } else {
17246               item.align[i] = null;
17247             }
17248           }
17249     
17250           for (i = 0; i < item.cells.length; i++) {
17251             item.cells[i] = item.cells[i]
17252               .replace(/^ *\| *| *\| *$/g, '')
17253               .split(/ *\| */);
17254           }
17255     
17256           this.tokens.push(item);
17257     
17258           continue;
17259         }
17260     
17261         // top-level paragraph
17262         if (top && (cap = this.rules.paragraph.exec(src))) {
17263           src = src.substring(cap[0].length);
17264           this.tokens.push({
17265             type: 'paragraph',
17266             text: cap[1].charAt(cap[1].length - 1) === '\n'
17267               ? cap[1].slice(0, -1)
17268               : cap[1]
17269           });
17270           continue;
17271         }
17272     
17273         // text
17274         if (cap = this.rules.text.exec(src)) {
17275           // Top-level should never reach here.
17276           src = src.substring(cap[0].length);
17277           this.tokens.push({
17278             type: 'text',
17279             text: cap[0]
17280           });
17281           continue;
17282         }
17283     
17284         if (src) {
17285           throw new
17286             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17287         }
17288       }
17289     
17290       return this.tokens;
17291     };
17292     
17293     /**
17294      * Inline-Level Grammar
17295      */
17296     
17297     var inline = {
17298       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17299       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17300       url: noop,
17301       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17302       link: /^!?\[(inside)\]\(href\)/,
17303       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17304       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17305       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17306       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17307       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17308       br: /^ {2,}\n(?!\s*$)/,
17309       del: noop,
17310       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17311     };
17312     
17313     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17314     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17315     
17316     inline.link = replace(inline.link)
17317       ('inside', inline._inside)
17318       ('href', inline._href)
17319       ();
17320     
17321     inline.reflink = replace(inline.reflink)
17322       ('inside', inline._inside)
17323       ();
17324     
17325     /**
17326      * Normal Inline Grammar
17327      */
17328     
17329     inline.normal = merge({}, inline);
17330     
17331     /**
17332      * Pedantic Inline Grammar
17333      */
17334     
17335     inline.pedantic = merge({}, inline.normal, {
17336       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17337       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17338     });
17339     
17340     /**
17341      * GFM Inline Grammar
17342      */
17343     
17344     inline.gfm = merge({}, inline.normal, {
17345       escape: replace(inline.escape)('])', '~|])')(),
17346       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17347       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17348       text: replace(inline.text)
17349         (']|', '~]|')
17350         ('|', '|https?://|')
17351         ()
17352     });
17353     
17354     /**
17355      * GFM + Line Breaks Inline Grammar
17356      */
17357     
17358     inline.breaks = merge({}, inline.gfm, {
17359       br: replace(inline.br)('{2,}', '*')(),
17360       text: replace(inline.gfm.text)('{2,}', '*')()
17361     });
17362     
17363     /**
17364      * Inline Lexer & Compiler
17365      */
17366     
17367     var InlineLexer  = function (links, options) {
17368       this.options = options || marked.defaults;
17369       this.links = links;
17370       this.rules = inline.normal;
17371       this.renderer = this.options.renderer || new Renderer;
17372       this.renderer.options = this.options;
17373     
17374       if (!this.links) {
17375         throw new
17376           Error('Tokens array requires a `links` property.');
17377       }
17378     
17379       if (this.options.gfm) {
17380         if (this.options.breaks) {
17381           this.rules = inline.breaks;
17382         } else {
17383           this.rules = inline.gfm;
17384         }
17385       } else if (this.options.pedantic) {
17386         this.rules = inline.pedantic;
17387       }
17388     }
17389     
17390     /**
17391      * Expose Inline Rules
17392      */
17393     
17394     InlineLexer.rules = inline;
17395     
17396     /**
17397      * Static Lexing/Compiling Method
17398      */
17399     
17400     InlineLexer.output = function(src, links, options) {
17401       var inline = new InlineLexer(links, options);
17402       return inline.output(src);
17403     };
17404     
17405     /**
17406      * Lexing/Compiling
17407      */
17408     
17409     InlineLexer.prototype.output = function(src) {
17410       var out = ''
17411         , link
17412         , text
17413         , href
17414         , cap;
17415     
17416       while (src) {
17417         // escape
17418         if (cap = this.rules.escape.exec(src)) {
17419           src = src.substring(cap[0].length);
17420           out += cap[1];
17421           continue;
17422         }
17423     
17424         // autolink
17425         if (cap = this.rules.autolink.exec(src)) {
17426           src = src.substring(cap[0].length);
17427           if (cap[2] === '@') {
17428             text = cap[1].charAt(6) === ':'
17429               ? this.mangle(cap[1].substring(7))
17430               : this.mangle(cap[1]);
17431             href = this.mangle('mailto:') + text;
17432           } else {
17433             text = escape(cap[1]);
17434             href = text;
17435           }
17436           out += this.renderer.link(href, null, text);
17437           continue;
17438         }
17439     
17440         // url (gfm)
17441         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17442           src = src.substring(cap[0].length);
17443           text = escape(cap[1]);
17444           href = text;
17445           out += this.renderer.link(href, null, text);
17446           continue;
17447         }
17448     
17449         // tag
17450         if (cap = this.rules.tag.exec(src)) {
17451           if (!this.inLink && /^<a /i.test(cap[0])) {
17452             this.inLink = true;
17453           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17454             this.inLink = false;
17455           }
17456           src = src.substring(cap[0].length);
17457           out += this.options.sanitize
17458             ? this.options.sanitizer
17459               ? this.options.sanitizer(cap[0])
17460               : escape(cap[0])
17461             : cap[0];
17462           continue;
17463         }
17464     
17465         // link
17466         if (cap = this.rules.link.exec(src)) {
17467           src = src.substring(cap[0].length);
17468           this.inLink = true;
17469           out += this.outputLink(cap, {
17470             href: cap[2],
17471             title: cap[3]
17472           });
17473           this.inLink = false;
17474           continue;
17475         }
17476     
17477         // reflink, nolink
17478         if ((cap = this.rules.reflink.exec(src))
17479             || (cap = this.rules.nolink.exec(src))) {
17480           src = src.substring(cap[0].length);
17481           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17482           link = this.links[link.toLowerCase()];
17483           if (!link || !link.href) {
17484             out += cap[0].charAt(0);
17485             src = cap[0].substring(1) + src;
17486             continue;
17487           }
17488           this.inLink = true;
17489           out += this.outputLink(cap, link);
17490           this.inLink = false;
17491           continue;
17492         }
17493     
17494         // strong
17495         if (cap = this.rules.strong.exec(src)) {
17496           src = src.substring(cap[0].length);
17497           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17498           continue;
17499         }
17500     
17501         // em
17502         if (cap = this.rules.em.exec(src)) {
17503           src = src.substring(cap[0].length);
17504           out += this.renderer.em(this.output(cap[2] || cap[1]));
17505           continue;
17506         }
17507     
17508         // code
17509         if (cap = this.rules.code.exec(src)) {
17510           src = src.substring(cap[0].length);
17511           out += this.renderer.codespan(escape(cap[2], true));
17512           continue;
17513         }
17514     
17515         // br
17516         if (cap = this.rules.br.exec(src)) {
17517           src = src.substring(cap[0].length);
17518           out += this.renderer.br();
17519           continue;
17520         }
17521     
17522         // del (gfm)
17523         if (cap = this.rules.del.exec(src)) {
17524           src = src.substring(cap[0].length);
17525           out += this.renderer.del(this.output(cap[1]));
17526           continue;
17527         }
17528     
17529         // text
17530         if (cap = this.rules.text.exec(src)) {
17531           src = src.substring(cap[0].length);
17532           out += this.renderer.text(escape(this.smartypants(cap[0])));
17533           continue;
17534         }
17535     
17536         if (src) {
17537           throw new
17538             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17539         }
17540       }
17541     
17542       return out;
17543     };
17544     
17545     /**
17546      * Compile Link
17547      */
17548     
17549     InlineLexer.prototype.outputLink = function(cap, link) {
17550       var href = escape(link.href)
17551         , title = link.title ? escape(link.title) : null;
17552     
17553       return cap[0].charAt(0) !== '!'
17554         ? this.renderer.link(href, title, this.output(cap[1]))
17555         : this.renderer.image(href, title, escape(cap[1]));
17556     };
17557     
17558     /**
17559      * Smartypants Transformations
17560      */
17561     
17562     InlineLexer.prototype.smartypants = function(text) {
17563       if (!this.options.smartypants)  { return text; }
17564       return text
17565         // em-dashes
17566         .replace(/---/g, '\u2014')
17567         // en-dashes
17568         .replace(/--/g, '\u2013')
17569         // opening singles
17570         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17571         // closing singles & apostrophes
17572         .replace(/'/g, '\u2019')
17573         // opening doubles
17574         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17575         // closing doubles
17576         .replace(/"/g, '\u201d')
17577         // ellipses
17578         .replace(/\.{3}/g, '\u2026');
17579     };
17580     
17581     /**
17582      * Mangle Links
17583      */
17584     
17585     InlineLexer.prototype.mangle = function(text) {
17586       if (!this.options.mangle) { return text; }
17587       var out = ''
17588         , l = text.length
17589         , i = 0
17590         , ch;
17591     
17592       for (; i < l; i++) {
17593         ch = text.charCodeAt(i);
17594         if (Math.random() > 0.5) {
17595           ch = 'x' + ch.toString(16);
17596         }
17597         out += '&#' + ch + ';';
17598       }
17599     
17600       return out;
17601     };
17602     
17603     /**
17604      * Renderer
17605      */
17606     
17607      /**
17608          * eval:var:Renderer
17609     */
17610     
17611     var Renderer   = function (options) {
17612       this.options = options || {};
17613     }
17614     
17615     Renderer.prototype.code = function(code, lang, escaped) {
17616       if (this.options.highlight) {
17617         var out = this.options.highlight(code, lang);
17618         if (out != null && out !== code) {
17619           escaped = true;
17620           code = out;
17621         }
17622       } else {
17623             // hack!!! - it's already escapeD?
17624             escaped = true;
17625       }
17626     
17627       if (!lang) {
17628         return '<pre><code>'
17629           + (escaped ? code : escape(code, true))
17630           + '\n</code></pre>';
17631       }
17632     
17633       return '<pre><code class="'
17634         + this.options.langPrefix
17635         + escape(lang, true)
17636         + '">'
17637         + (escaped ? code : escape(code, true))
17638         + '\n</code></pre>\n';
17639     };
17640     
17641     Renderer.prototype.blockquote = function(quote) {
17642       return '<blockquote>\n' + quote + '</blockquote>\n';
17643     };
17644     
17645     Renderer.prototype.html = function(html) {
17646       return html;
17647     };
17648     
17649     Renderer.prototype.heading = function(text, level, raw) {
17650       return '<h'
17651         + level
17652         + ' id="'
17653         + this.options.headerPrefix
17654         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17655         + '">'
17656         + text
17657         + '</h'
17658         + level
17659         + '>\n';
17660     };
17661     
17662     Renderer.prototype.hr = function() {
17663       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17664     };
17665     
17666     Renderer.prototype.list = function(body, ordered) {
17667       var type = ordered ? 'ol' : 'ul';
17668       return '<' + type + '>\n' + body + '</' + type + '>\n';
17669     };
17670     
17671     Renderer.prototype.listitem = function(text) {
17672       return '<li>' + text + '</li>\n';
17673     };
17674     
17675     Renderer.prototype.paragraph = function(text) {
17676       return '<p>' + text + '</p>\n';
17677     };
17678     
17679     Renderer.prototype.table = function(header, body) {
17680       return '<table class="table table-striped">\n'
17681         + '<thead>\n'
17682         + header
17683         + '</thead>\n'
17684         + '<tbody>\n'
17685         + body
17686         + '</tbody>\n'
17687         + '</table>\n';
17688     };
17689     
17690     Renderer.prototype.tablerow = function(content) {
17691       return '<tr>\n' + content + '</tr>\n';
17692     };
17693     
17694     Renderer.prototype.tablecell = function(content, flags) {
17695       var type = flags.header ? 'th' : 'td';
17696       var tag = flags.align
17697         ? '<' + type + ' style="text-align:' + flags.align + '">'
17698         : '<' + type + '>';
17699       return tag + content + '</' + type + '>\n';
17700     };
17701     
17702     // span level renderer
17703     Renderer.prototype.strong = function(text) {
17704       return '<strong>' + text + '</strong>';
17705     };
17706     
17707     Renderer.prototype.em = function(text) {
17708       return '<em>' + text + '</em>';
17709     };
17710     
17711     Renderer.prototype.codespan = function(text) {
17712       return '<code>' + text + '</code>';
17713     };
17714     
17715     Renderer.prototype.br = function() {
17716       return this.options.xhtml ? '<br/>' : '<br>';
17717     };
17718     
17719     Renderer.prototype.del = function(text) {
17720       return '<del>' + text + '</del>';
17721     };
17722     
17723     Renderer.prototype.link = function(href, title, text) {
17724       if (this.options.sanitize) {
17725         try {
17726           var prot = decodeURIComponent(unescape(href))
17727             .replace(/[^\w:]/g, '')
17728             .toLowerCase();
17729         } catch (e) {
17730           return '';
17731         }
17732         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17733           return '';
17734         }
17735       }
17736       var out = '<a href="' + href + '"';
17737       if (title) {
17738         out += ' title="' + title + '"';
17739       }
17740       out += '>' + text + '</a>';
17741       return out;
17742     };
17743     
17744     Renderer.prototype.image = function(href, title, text) {
17745       var out = '<img src="' + href + '" alt="' + text + '"';
17746       if (title) {
17747         out += ' title="' + title + '"';
17748       }
17749       out += this.options.xhtml ? '/>' : '>';
17750       return out;
17751     };
17752     
17753     Renderer.prototype.text = function(text) {
17754       return text;
17755     };
17756     
17757     /**
17758      * Parsing & Compiling
17759      */
17760          /**
17761          * eval:var:Parser
17762     */
17763     
17764     var Parser= function (options) {
17765       this.tokens = [];
17766       this.token = null;
17767       this.options = options || marked.defaults;
17768       this.options.renderer = this.options.renderer || new Renderer;
17769       this.renderer = this.options.renderer;
17770       this.renderer.options = this.options;
17771     }
17772     
17773     /**
17774      * Static Parse Method
17775      */
17776     
17777     Parser.parse = function(src, options, renderer) {
17778       var parser = new Parser(options, renderer);
17779       return parser.parse(src);
17780     };
17781     
17782     /**
17783      * Parse Loop
17784      */
17785     
17786     Parser.prototype.parse = function(src) {
17787       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17788       this.tokens = src.reverse();
17789     
17790       var out = '';
17791       while (this.next()) {
17792         out += this.tok();
17793       }
17794     
17795       return out;
17796     };
17797     
17798     /**
17799      * Next Token
17800      */
17801     
17802     Parser.prototype.next = function() {
17803       return this.token = this.tokens.pop();
17804     };
17805     
17806     /**
17807      * Preview Next Token
17808      */
17809     
17810     Parser.prototype.peek = function() {
17811       return this.tokens[this.tokens.length - 1] || 0;
17812     };
17813     
17814     /**
17815      * Parse Text Tokens
17816      */
17817     
17818     Parser.prototype.parseText = function() {
17819       var body = this.token.text;
17820     
17821       while (this.peek().type === 'text') {
17822         body += '\n' + this.next().text;
17823       }
17824     
17825       return this.inline.output(body);
17826     };
17827     
17828     /**
17829      * Parse Current Token
17830      */
17831     
17832     Parser.prototype.tok = function() {
17833       switch (this.token.type) {
17834         case 'space': {
17835           return '';
17836         }
17837         case 'hr': {
17838           return this.renderer.hr();
17839         }
17840         case 'heading': {
17841           return this.renderer.heading(
17842             this.inline.output(this.token.text),
17843             this.token.depth,
17844             this.token.text);
17845         }
17846         case 'code': {
17847           return this.renderer.code(this.token.text,
17848             this.token.lang,
17849             this.token.escaped);
17850         }
17851         case 'table': {
17852           var header = ''
17853             , body = ''
17854             , i
17855             , row
17856             , cell
17857             , flags
17858             , j;
17859     
17860           // header
17861           cell = '';
17862           for (i = 0; i < this.token.header.length; i++) {
17863             flags = { header: true, align: this.token.align[i] };
17864             cell += this.renderer.tablecell(
17865               this.inline.output(this.token.header[i]),
17866               { header: true, align: this.token.align[i] }
17867             );
17868           }
17869           header += this.renderer.tablerow(cell);
17870     
17871           for (i = 0; i < this.token.cells.length; i++) {
17872             row = this.token.cells[i];
17873     
17874             cell = '';
17875             for (j = 0; j < row.length; j++) {
17876               cell += this.renderer.tablecell(
17877                 this.inline.output(row[j]),
17878                 { header: false, align: this.token.align[j] }
17879               );
17880             }
17881     
17882             body += this.renderer.tablerow(cell);
17883           }
17884           return this.renderer.table(header, body);
17885         }
17886         case 'blockquote_start': {
17887           var body = '';
17888     
17889           while (this.next().type !== 'blockquote_end') {
17890             body += this.tok();
17891           }
17892     
17893           return this.renderer.blockquote(body);
17894         }
17895         case 'list_start': {
17896           var body = ''
17897             , ordered = this.token.ordered;
17898     
17899           while (this.next().type !== 'list_end') {
17900             body += this.tok();
17901           }
17902     
17903           return this.renderer.list(body, ordered);
17904         }
17905         case 'list_item_start': {
17906           var body = '';
17907     
17908           while (this.next().type !== 'list_item_end') {
17909             body += this.token.type === 'text'
17910               ? this.parseText()
17911               : this.tok();
17912           }
17913     
17914           return this.renderer.listitem(body);
17915         }
17916         case 'loose_item_start': {
17917           var body = '';
17918     
17919           while (this.next().type !== 'list_item_end') {
17920             body += this.tok();
17921           }
17922     
17923           return this.renderer.listitem(body);
17924         }
17925         case 'html': {
17926           var html = !this.token.pre && !this.options.pedantic
17927             ? this.inline.output(this.token.text)
17928             : this.token.text;
17929           return this.renderer.html(html);
17930         }
17931         case 'paragraph': {
17932           return this.renderer.paragraph(this.inline.output(this.token.text));
17933         }
17934         case 'text': {
17935           return this.renderer.paragraph(this.parseText());
17936         }
17937       }
17938     };
17939   
17940          /**
17941          * eval:var:noop
17942     */
17943     var noop = function () {}
17944     noop.exec = noop;
17945     
17946          /**
17947          * eval:var:merge
17948     */
17949     var merge = function (obj) {
17950       var i = 1
17951         , target
17952         , key;
17953     
17954       for (; i < arguments.length; i++) {
17955         target = arguments[i];
17956         for (key in target) {
17957           if (Object.prototype.hasOwnProperty.call(target, key)) {
17958             obj[key] = target[key];
17959           }
17960         }
17961       }
17962     
17963       return obj;
17964     }
17965     
17966     
17967     /**
17968      * Marked
17969      */
17970          /**
17971          * eval:var:marked
17972     */
17973     var marked = function (src, opt, callback) {
17974       if (callback || typeof opt === 'function') {
17975         if (!callback) {
17976           callback = opt;
17977           opt = null;
17978         }
17979     
17980         opt = merge({}, marked.defaults, opt || {});
17981     
17982         var highlight = opt.highlight
17983           , tokens
17984           , pending
17985           , i = 0;
17986     
17987         try {
17988           tokens = Lexer.lex(src, opt)
17989         } catch (e) {
17990           return callback(e);
17991         }
17992     
17993         pending = tokens.length;
17994          /**
17995          * eval:var:done
17996     */
17997         var done = function(err) {
17998           if (err) {
17999             opt.highlight = highlight;
18000             return callback(err);
18001           }
18002     
18003           var out;
18004     
18005           try {
18006             out = Parser.parse(tokens, opt);
18007           } catch (e) {
18008             err = e;
18009           }
18010     
18011           opt.highlight = highlight;
18012     
18013           return err
18014             ? callback(err)
18015             : callback(null, out);
18016         };
18017     
18018         if (!highlight || highlight.length < 3) {
18019           return done();
18020         }
18021     
18022         delete opt.highlight;
18023     
18024         if (!pending) { return done(); }
18025     
18026         for (; i < tokens.length; i++) {
18027           (function(token) {
18028             if (token.type !== 'code') {
18029               return --pending || done();
18030             }
18031             return highlight(token.text, token.lang, function(err, code) {
18032               if (err) { return done(err); }
18033               if (code == null || code === token.text) {
18034                 return --pending || done();
18035               }
18036               token.text = code;
18037               token.escaped = true;
18038               --pending || done();
18039             });
18040           })(tokens[i]);
18041         }
18042     
18043         return;
18044       }
18045       try {
18046         if (opt) { opt = merge({}, marked.defaults, opt); }
18047         return Parser.parse(Lexer.lex(src, opt), opt);
18048       } catch (e) {
18049         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18050         if ((opt || marked.defaults).silent) {
18051           return '<p>An error occured:</p><pre>'
18052             + escape(e.message + '', true)
18053             + '</pre>';
18054         }
18055         throw e;
18056       }
18057     }
18058     
18059     /**
18060      * Options
18061      */
18062     
18063     marked.options =
18064     marked.setOptions = function(opt) {
18065       merge(marked.defaults, opt);
18066       return marked;
18067     };
18068     
18069     marked.defaults = {
18070       gfm: true,
18071       tables: true,
18072       breaks: false,
18073       pedantic: false,
18074       sanitize: false,
18075       sanitizer: null,
18076       mangle: true,
18077       smartLists: false,
18078       silent: false,
18079       highlight: null,
18080       langPrefix: 'lang-',
18081       smartypants: false,
18082       headerPrefix: '',
18083       renderer: new Renderer,
18084       xhtml: false
18085     };
18086     
18087     /**
18088      * Expose
18089      */
18090     
18091     marked.Parser = Parser;
18092     marked.parser = Parser.parse;
18093     
18094     marked.Renderer = Renderer;
18095     
18096     marked.Lexer = Lexer;
18097     marked.lexer = Lexer.lex;
18098     
18099     marked.InlineLexer = InlineLexer;
18100     marked.inlineLexer = InlineLexer.output;
18101     
18102     marked.parse = marked;
18103     
18104     Roo.Markdown.marked = marked;
18105
18106 })();/*
18107  * Based on:
18108  * Ext JS Library 1.1.1
18109  * Copyright(c) 2006-2007, Ext JS, LLC.
18110  *
18111  * Originally Released Under LGPL - original licence link has changed is not relivant.
18112  *
18113  * Fork - LGPL
18114  * <script type="text/javascript">
18115  */
18116
18117
18118
18119 /*
18120  * These classes are derivatives of the similarly named classes in the YUI Library.
18121  * The original license:
18122  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18123  * Code licensed under the BSD License:
18124  * http://developer.yahoo.net/yui/license.txt
18125  */
18126
18127 (function() {
18128
18129 var Event=Roo.EventManager;
18130 var Dom=Roo.lib.Dom;
18131
18132 /**
18133  * @class Roo.dd.DragDrop
18134  * @extends Roo.util.Observable
18135  * Defines the interface and base operation of items that that can be
18136  * dragged or can be drop targets.  It was designed to be extended, overriding
18137  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18138  * Up to three html elements can be associated with a DragDrop instance:
18139  * <ul>
18140  * <li>linked element: the element that is passed into the constructor.
18141  * This is the element which defines the boundaries for interaction with
18142  * other DragDrop objects.</li>
18143  * <li>handle element(s): The drag operation only occurs if the element that
18144  * was clicked matches a handle element.  By default this is the linked
18145  * element, but there are times that you will want only a portion of the
18146  * linked element to initiate the drag operation, and the setHandleElId()
18147  * method provides a way to define this.</li>
18148  * <li>drag element: this represents the element that would be moved along
18149  * with the cursor during a drag operation.  By default, this is the linked
18150  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18151  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18152  * </li>
18153  * </ul>
18154  * This class should not be instantiated until the onload event to ensure that
18155  * the associated elements are available.
18156  * The following would define a DragDrop obj that would interact with any
18157  * other DragDrop obj in the "group1" group:
18158  * <pre>
18159  *  dd = new Roo.dd.DragDrop("div1", "group1");
18160  * </pre>
18161  * Since none of the event handlers have been implemented, nothing would
18162  * actually happen if you were to run the code above.  Normally you would
18163  * override this class or one of the default implementations, but you can
18164  * also override the methods you want on an instance of the class...
18165  * <pre>
18166  *  dd.onDragDrop = function(e, id) {
18167  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18168  *  }
18169  * </pre>
18170  * @constructor
18171  * @param {String} id of the element that is linked to this instance
18172  * @param {String} sGroup the group of related DragDrop objects
18173  * @param {object} config an object containing configurable attributes
18174  *                Valid properties for DragDrop:
18175  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18176  */
18177 Roo.dd.DragDrop = function(id, sGroup, config) {
18178     if (id) {
18179         this.init(id, sGroup, config);
18180     }
18181     
18182 };
18183
18184 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18185
18186     /**
18187      * The id of the element associated with this object.  This is what we
18188      * refer to as the "linked element" because the size and position of
18189      * this element is used to determine when the drag and drop objects have
18190      * interacted.
18191      * @property id
18192      * @type String
18193      */
18194     id: null,
18195
18196     /**
18197      * Configuration attributes passed into the constructor
18198      * @property config
18199      * @type object
18200      */
18201     config: null,
18202
18203     /**
18204      * The id of the element that will be dragged.  By default this is same
18205      * as the linked element , but could be changed to another element. Ex:
18206      * Roo.dd.DDProxy
18207      * @property dragElId
18208      * @type String
18209      * @private
18210      */
18211     dragElId: null,
18212
18213     /**
18214      * the id of the element that initiates the drag operation.  By default
18215      * this is the linked element, but could be changed to be a child of this
18216      * element.  This lets us do things like only starting the drag when the
18217      * header element within the linked html element is clicked.
18218      * @property handleElId
18219      * @type String
18220      * @private
18221      */
18222     handleElId: null,
18223
18224     /**
18225      * An associative array of HTML tags that will be ignored if clicked.
18226      * @property invalidHandleTypes
18227      * @type {string: string}
18228      */
18229     invalidHandleTypes: null,
18230
18231     /**
18232      * An associative array of ids for elements that will be ignored if clicked
18233      * @property invalidHandleIds
18234      * @type {string: string}
18235      */
18236     invalidHandleIds: null,
18237
18238     /**
18239      * An indexted array of css class names for elements that will be ignored
18240      * if clicked.
18241      * @property invalidHandleClasses
18242      * @type string[]
18243      */
18244     invalidHandleClasses: null,
18245
18246     /**
18247      * The linked element's absolute X position at the time the drag was
18248      * started
18249      * @property startPageX
18250      * @type int
18251      * @private
18252      */
18253     startPageX: 0,
18254
18255     /**
18256      * The linked element's absolute X position at the time the drag was
18257      * started
18258      * @property startPageY
18259      * @type int
18260      * @private
18261      */
18262     startPageY: 0,
18263
18264     /**
18265      * The group defines a logical collection of DragDrop objects that are
18266      * related.  Instances only get events when interacting with other
18267      * DragDrop object in the same group.  This lets us define multiple
18268      * groups using a single DragDrop subclass if we want.
18269      * @property groups
18270      * @type {string: string}
18271      */
18272     groups: null,
18273
18274     /**
18275      * Individual drag/drop instances can be locked.  This will prevent
18276      * onmousedown start drag.
18277      * @property locked
18278      * @type boolean
18279      * @private
18280      */
18281     locked: false,
18282
18283     /**
18284      * Lock this instance
18285      * @method lock
18286      */
18287     lock: function() { this.locked = true; },
18288
18289     /**
18290      * Unlock this instace
18291      * @method unlock
18292      */
18293     unlock: function() { this.locked = false; },
18294
18295     /**
18296      * By default, all insances can be a drop target.  This can be disabled by
18297      * setting isTarget to false.
18298      * @method isTarget
18299      * @type boolean
18300      */
18301     isTarget: true,
18302
18303     /**
18304      * The padding configured for this drag and drop object for calculating
18305      * the drop zone intersection with this object.
18306      * @method padding
18307      * @type int[]
18308      */
18309     padding: null,
18310
18311     /**
18312      * Cached reference to the linked element
18313      * @property _domRef
18314      * @private
18315      */
18316     _domRef: null,
18317
18318     /**
18319      * Internal typeof flag
18320      * @property __ygDragDrop
18321      * @private
18322      */
18323     __ygDragDrop: true,
18324
18325     /**
18326      * Set to true when horizontal contraints are applied
18327      * @property constrainX
18328      * @type boolean
18329      * @private
18330      */
18331     constrainX: false,
18332
18333     /**
18334      * Set to true when vertical contraints are applied
18335      * @property constrainY
18336      * @type boolean
18337      * @private
18338      */
18339     constrainY: false,
18340
18341     /**
18342      * The left constraint
18343      * @property minX
18344      * @type int
18345      * @private
18346      */
18347     minX: 0,
18348
18349     /**
18350      * The right constraint
18351      * @property maxX
18352      * @type int
18353      * @private
18354      */
18355     maxX: 0,
18356
18357     /**
18358      * The up constraint
18359      * @property minY
18360      * @type int
18361      * @type int
18362      * @private
18363      */
18364     minY: 0,
18365
18366     /**
18367      * The down constraint
18368      * @property maxY
18369      * @type int
18370      * @private
18371      */
18372     maxY: 0,
18373
18374     /**
18375      * Maintain offsets when we resetconstraints.  Set to true when you want
18376      * the position of the element relative to its parent to stay the same
18377      * when the page changes
18378      *
18379      * @property maintainOffset
18380      * @type boolean
18381      */
18382     maintainOffset: false,
18383
18384     /**
18385      * Array of pixel locations the element will snap to if we specified a
18386      * horizontal graduation/interval.  This array is generated automatically
18387      * when you define a tick interval.
18388      * @property xTicks
18389      * @type int[]
18390      */
18391     xTicks: null,
18392
18393     /**
18394      * Array of pixel locations the element will snap to if we specified a
18395      * vertical graduation/interval.  This array is generated automatically
18396      * when you define a tick interval.
18397      * @property yTicks
18398      * @type int[]
18399      */
18400     yTicks: null,
18401
18402     /**
18403      * By default the drag and drop instance will only respond to the primary
18404      * button click (left button for a right-handed mouse).  Set to true to
18405      * allow drag and drop to start with any mouse click that is propogated
18406      * by the browser
18407      * @property primaryButtonOnly
18408      * @type boolean
18409      */
18410     primaryButtonOnly: true,
18411
18412     /**
18413      * The availabe property is false until the linked dom element is accessible.
18414      * @property available
18415      * @type boolean
18416      */
18417     available: false,
18418
18419     /**
18420      * By default, drags can only be initiated if the mousedown occurs in the
18421      * region the linked element is.  This is done in part to work around a
18422      * bug in some browsers that mis-report the mousedown if the previous
18423      * mouseup happened outside of the window.  This property is set to true
18424      * if outer handles are defined.
18425      *
18426      * @property hasOuterHandles
18427      * @type boolean
18428      * @default false
18429      */
18430     hasOuterHandles: false,
18431
18432     /**
18433      * Code that executes immediately before the startDrag event
18434      * @method b4StartDrag
18435      * @private
18436      */
18437     b4StartDrag: function(x, y) { },
18438
18439     /**
18440      * Abstract method called after a drag/drop object is clicked
18441      * and the drag or mousedown time thresholds have beeen met.
18442      * @method startDrag
18443      * @param {int} X click location
18444      * @param {int} Y click location
18445      */
18446     startDrag: function(x, y) { /* override this */ },
18447
18448     /**
18449      * Code that executes immediately before the onDrag event
18450      * @method b4Drag
18451      * @private
18452      */
18453     b4Drag: function(e) { },
18454
18455     /**
18456      * Abstract method called during the onMouseMove event while dragging an
18457      * object.
18458      * @method onDrag
18459      * @param {Event} e the mousemove event
18460      */
18461     onDrag: function(e) { /* override this */ },
18462
18463     /**
18464      * Abstract method called when this element fist begins hovering over
18465      * another DragDrop obj
18466      * @method onDragEnter
18467      * @param {Event} e the mousemove event
18468      * @param {String|DragDrop[]} id In POINT mode, the element
18469      * id this is hovering over.  In INTERSECT mode, an array of one or more
18470      * dragdrop items being hovered over.
18471      */
18472     onDragEnter: function(e, id) { /* override this */ },
18473
18474     /**
18475      * Code that executes immediately before the onDragOver event
18476      * @method b4DragOver
18477      * @private
18478      */
18479     b4DragOver: function(e) { },
18480
18481     /**
18482      * Abstract method called when this element is hovering over another
18483      * DragDrop obj
18484      * @method onDragOver
18485      * @param {Event} e the mousemove event
18486      * @param {String|DragDrop[]} id In POINT mode, the element
18487      * id this is hovering over.  In INTERSECT mode, an array of dd items
18488      * being hovered over.
18489      */
18490     onDragOver: function(e, id) { /* override this */ },
18491
18492     /**
18493      * Code that executes immediately before the onDragOut event
18494      * @method b4DragOut
18495      * @private
18496      */
18497     b4DragOut: function(e) { },
18498
18499     /**
18500      * Abstract method called when we are no longer hovering over an element
18501      * @method onDragOut
18502      * @param {Event} e the mousemove event
18503      * @param {String|DragDrop[]} id In POINT mode, the element
18504      * id this was hovering over.  In INTERSECT mode, an array of dd items
18505      * that the mouse is no longer over.
18506      */
18507     onDragOut: function(e, id) { /* override this */ },
18508
18509     /**
18510      * Code that executes immediately before the onDragDrop event
18511      * @method b4DragDrop
18512      * @private
18513      */
18514     b4DragDrop: function(e) { },
18515
18516     /**
18517      * Abstract method called when this item is dropped on another DragDrop
18518      * obj
18519      * @method onDragDrop
18520      * @param {Event} e the mouseup event
18521      * @param {String|DragDrop[]} id In POINT mode, the element
18522      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18523      * was dropped on.
18524      */
18525     onDragDrop: function(e, id) { /* override this */ },
18526
18527     /**
18528      * Abstract method called when this item is dropped on an area with no
18529      * drop target
18530      * @method onInvalidDrop
18531      * @param {Event} e the mouseup event
18532      */
18533     onInvalidDrop: function(e) { /* override this */ },
18534
18535     /**
18536      * Code that executes immediately before the endDrag event
18537      * @method b4EndDrag
18538      * @private
18539      */
18540     b4EndDrag: function(e) { },
18541
18542     /**
18543      * Fired when we are done dragging the object
18544      * @method endDrag
18545      * @param {Event} e the mouseup event
18546      */
18547     endDrag: function(e) { /* override this */ },
18548
18549     /**
18550      * Code executed immediately before the onMouseDown event
18551      * @method b4MouseDown
18552      * @param {Event} e the mousedown event
18553      * @private
18554      */
18555     b4MouseDown: function(e) {  },
18556
18557     /**
18558      * Event handler that fires when a drag/drop obj gets a mousedown
18559      * @method onMouseDown
18560      * @param {Event} e the mousedown event
18561      */
18562     onMouseDown: function(e) { /* override this */ },
18563
18564     /**
18565      * Event handler that fires when a drag/drop obj gets a mouseup
18566      * @method onMouseUp
18567      * @param {Event} e the mouseup event
18568      */
18569     onMouseUp: function(e) { /* override this */ },
18570
18571     /**
18572      * Override the onAvailable method to do what is needed after the initial
18573      * position was determined.
18574      * @method onAvailable
18575      */
18576     onAvailable: function () {
18577     },
18578
18579     /*
18580      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18581      * @type Object
18582      */
18583     defaultPadding : {left:0, right:0, top:0, bottom:0},
18584
18585     /*
18586      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18587  *
18588  * Usage:
18589  <pre><code>
18590  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18591                 { dragElId: "existingProxyDiv" });
18592  dd.startDrag = function(){
18593      this.constrainTo("parent-id");
18594  };
18595  </code></pre>
18596  * Or you can initalize it using the {@link Roo.Element} object:
18597  <pre><code>
18598  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18599      startDrag : function(){
18600          this.constrainTo("parent-id");
18601      }
18602  });
18603  </code></pre>
18604      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18605      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18606      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18607      * an object containing the sides to pad. For example: {right:10, bottom:10}
18608      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18609      */
18610     constrainTo : function(constrainTo, pad, inContent){
18611         if(typeof pad == "number"){
18612             pad = {left: pad, right:pad, top:pad, bottom:pad};
18613         }
18614         pad = pad || this.defaultPadding;
18615         var b = Roo.get(this.getEl()).getBox();
18616         var ce = Roo.get(constrainTo);
18617         var s = ce.getScroll();
18618         var c, cd = ce.dom;
18619         if(cd == document.body){
18620             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18621         }else{
18622             xy = ce.getXY();
18623             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18624         }
18625
18626
18627         var topSpace = b.y - c.y;
18628         var leftSpace = b.x - c.x;
18629
18630         this.resetConstraints();
18631         this.setXConstraint(leftSpace - (pad.left||0), // left
18632                 c.width - leftSpace - b.width - (pad.right||0) //right
18633         );
18634         this.setYConstraint(topSpace - (pad.top||0), //top
18635                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18636         );
18637     },
18638
18639     /**
18640      * Returns a reference to the linked element
18641      * @method getEl
18642      * @return {HTMLElement} the html element
18643      */
18644     getEl: function() {
18645         if (!this._domRef) {
18646             this._domRef = Roo.getDom(this.id);
18647         }
18648
18649         return this._domRef;
18650     },
18651
18652     /**
18653      * Returns a reference to the actual element to drag.  By default this is
18654      * the same as the html element, but it can be assigned to another
18655      * element. An example of this can be found in Roo.dd.DDProxy
18656      * @method getDragEl
18657      * @return {HTMLElement} the html element
18658      */
18659     getDragEl: function() {
18660         return Roo.getDom(this.dragElId);
18661     },
18662
18663     /**
18664      * Sets up the DragDrop object.  Must be called in the constructor of any
18665      * Roo.dd.DragDrop subclass
18666      * @method init
18667      * @param id the id of the linked element
18668      * @param {String} sGroup the group of related items
18669      * @param {object} config configuration attributes
18670      */
18671     init: function(id, sGroup, config) {
18672         this.initTarget(id, sGroup, config);
18673         if (!Roo.isTouch) {
18674             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18675         }
18676         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18677         // Event.on(this.id, "selectstart", Event.preventDefault);
18678     },
18679
18680     /**
18681      * Initializes Targeting functionality only... the object does not
18682      * get a mousedown handler.
18683      * @method initTarget
18684      * @param id the id of the linked element
18685      * @param {String} sGroup the group of related items
18686      * @param {object} config configuration attributes
18687      */
18688     initTarget: function(id, sGroup, config) {
18689
18690         // configuration attributes
18691         this.config = config || {};
18692
18693         // create a local reference to the drag and drop manager
18694         this.DDM = Roo.dd.DDM;
18695         // initialize the groups array
18696         this.groups = {};
18697
18698         // assume that we have an element reference instead of an id if the
18699         // parameter is not a string
18700         if (typeof id !== "string") {
18701             id = Roo.id(id);
18702         }
18703
18704         // set the id
18705         this.id = id;
18706
18707         // add to an interaction group
18708         this.addToGroup((sGroup) ? sGroup : "default");
18709
18710         // We don't want to register this as the handle with the manager
18711         // so we just set the id rather than calling the setter.
18712         this.handleElId = id;
18713
18714         // the linked element is the element that gets dragged by default
18715         this.setDragElId(id);
18716
18717         // by default, clicked anchors will not start drag operations.
18718         this.invalidHandleTypes = { A: "A" };
18719         this.invalidHandleIds = {};
18720         this.invalidHandleClasses = [];
18721
18722         this.applyConfig();
18723
18724         this.handleOnAvailable();
18725     },
18726
18727     /**
18728      * Applies the configuration parameters that were passed into the constructor.
18729      * This is supposed to happen at each level through the inheritance chain.  So
18730      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18731      * DragDrop in order to get all of the parameters that are available in
18732      * each object.
18733      * @method applyConfig
18734      */
18735     applyConfig: function() {
18736
18737         // configurable properties:
18738         //    padding, isTarget, maintainOffset, primaryButtonOnly
18739         this.padding           = this.config.padding || [0, 0, 0, 0];
18740         this.isTarget          = (this.config.isTarget !== false);
18741         this.maintainOffset    = (this.config.maintainOffset);
18742         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18743
18744     },
18745
18746     /**
18747      * Executed when the linked element is available
18748      * @method handleOnAvailable
18749      * @private
18750      */
18751     handleOnAvailable: function() {
18752         this.available = true;
18753         this.resetConstraints();
18754         this.onAvailable();
18755     },
18756
18757      /**
18758      * Configures the padding for the target zone in px.  Effectively expands
18759      * (or reduces) the virtual object size for targeting calculations.
18760      * Supports css-style shorthand; if only one parameter is passed, all sides
18761      * will have that padding, and if only two are passed, the top and bottom
18762      * will have the first param, the left and right the second.
18763      * @method setPadding
18764      * @param {int} iTop    Top pad
18765      * @param {int} iRight  Right pad
18766      * @param {int} iBot    Bot pad
18767      * @param {int} iLeft   Left pad
18768      */
18769     setPadding: function(iTop, iRight, iBot, iLeft) {
18770         // this.padding = [iLeft, iRight, iTop, iBot];
18771         if (!iRight && 0 !== iRight) {
18772             this.padding = [iTop, iTop, iTop, iTop];
18773         } else if (!iBot && 0 !== iBot) {
18774             this.padding = [iTop, iRight, iTop, iRight];
18775         } else {
18776             this.padding = [iTop, iRight, iBot, iLeft];
18777         }
18778     },
18779
18780     /**
18781      * Stores the initial placement of the linked element.
18782      * @method setInitialPosition
18783      * @param {int} diffX   the X offset, default 0
18784      * @param {int} diffY   the Y offset, default 0
18785      */
18786     setInitPosition: function(diffX, diffY) {
18787         var el = this.getEl();
18788
18789         if (!this.DDM.verifyEl(el)) {
18790             return;
18791         }
18792
18793         var dx = diffX || 0;
18794         var dy = diffY || 0;
18795
18796         var p = Dom.getXY( el );
18797
18798         this.initPageX = p[0] - dx;
18799         this.initPageY = p[1] - dy;
18800
18801         this.lastPageX = p[0];
18802         this.lastPageY = p[1];
18803
18804
18805         this.setStartPosition(p);
18806     },
18807
18808     /**
18809      * Sets the start position of the element.  This is set when the obj
18810      * is initialized, the reset when a drag is started.
18811      * @method setStartPosition
18812      * @param pos current position (from previous lookup)
18813      * @private
18814      */
18815     setStartPosition: function(pos) {
18816         var p = pos || Dom.getXY( this.getEl() );
18817         this.deltaSetXY = null;
18818
18819         this.startPageX = p[0];
18820         this.startPageY = p[1];
18821     },
18822
18823     /**
18824      * Add this instance to a group of related drag/drop objects.  All
18825      * instances belong to at least one group, and can belong to as many
18826      * groups as needed.
18827      * @method addToGroup
18828      * @param sGroup {string} the name of the group
18829      */
18830     addToGroup: function(sGroup) {
18831         this.groups[sGroup] = true;
18832         this.DDM.regDragDrop(this, sGroup);
18833     },
18834
18835     /**
18836      * Remove's this instance from the supplied interaction group
18837      * @method removeFromGroup
18838      * @param {string}  sGroup  The group to drop
18839      */
18840     removeFromGroup: function(sGroup) {
18841         if (this.groups[sGroup]) {
18842             delete this.groups[sGroup];
18843         }
18844
18845         this.DDM.removeDDFromGroup(this, sGroup);
18846     },
18847
18848     /**
18849      * Allows you to specify that an element other than the linked element
18850      * will be moved with the cursor during a drag
18851      * @method setDragElId
18852      * @param id {string} the id of the element that will be used to initiate the drag
18853      */
18854     setDragElId: function(id) {
18855         this.dragElId = id;
18856     },
18857
18858     /**
18859      * Allows you to specify a child of the linked element that should be
18860      * used to initiate the drag operation.  An example of this would be if
18861      * you have a content div with text and links.  Clicking anywhere in the
18862      * content area would normally start the drag operation.  Use this method
18863      * to specify that an element inside of the content div is the element
18864      * that starts the drag operation.
18865      * @method setHandleElId
18866      * @param id {string} the id of the element that will be used to
18867      * initiate the drag.
18868      */
18869     setHandleElId: function(id) {
18870         if (typeof id !== "string") {
18871             id = Roo.id(id);
18872         }
18873         this.handleElId = id;
18874         this.DDM.regHandle(this.id, id);
18875     },
18876
18877     /**
18878      * Allows you to set an element outside of the linked element as a drag
18879      * handle
18880      * @method setOuterHandleElId
18881      * @param id the id of the element that will be used to initiate the drag
18882      */
18883     setOuterHandleElId: function(id) {
18884         if (typeof id !== "string") {
18885             id = Roo.id(id);
18886         }
18887         Event.on(id, "mousedown",
18888                 this.handleMouseDown, this);
18889         this.setHandleElId(id);
18890
18891         this.hasOuterHandles = true;
18892     },
18893
18894     /**
18895      * Remove all drag and drop hooks for this element
18896      * @method unreg
18897      */
18898     unreg: function() {
18899         Event.un(this.id, "mousedown",
18900                 this.handleMouseDown);
18901         Event.un(this.id, "touchstart",
18902                 this.handleMouseDown);
18903         this._domRef = null;
18904         this.DDM._remove(this);
18905     },
18906
18907     destroy : function(){
18908         this.unreg();
18909     },
18910
18911     /**
18912      * Returns true if this instance is locked, or the drag drop mgr is locked
18913      * (meaning that all drag/drop is disabled on the page.)
18914      * @method isLocked
18915      * @return {boolean} true if this obj or all drag/drop is locked, else
18916      * false
18917      */
18918     isLocked: function() {
18919         return (this.DDM.isLocked() || this.locked);
18920     },
18921
18922     /**
18923      * Fired when this object is clicked
18924      * @method handleMouseDown
18925      * @param {Event} e
18926      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18927      * @private
18928      */
18929     handleMouseDown: function(e, oDD){
18930      
18931         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18932             //Roo.log('not touch/ button !=0');
18933             return;
18934         }
18935         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18936             return; // double touch..
18937         }
18938         
18939
18940         if (this.isLocked()) {
18941             //Roo.log('locked');
18942             return;
18943         }
18944
18945         this.DDM.refreshCache(this.groups);
18946 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18947         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18948         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18949             //Roo.log('no outer handes or not over target');
18950                 // do nothing.
18951         } else {
18952 //            Roo.log('check validator');
18953             if (this.clickValidator(e)) {
18954 //                Roo.log('validate success');
18955                 // set the initial element position
18956                 this.setStartPosition();
18957
18958
18959                 this.b4MouseDown(e);
18960                 this.onMouseDown(e);
18961
18962                 this.DDM.handleMouseDown(e, this);
18963
18964                 this.DDM.stopEvent(e);
18965             } else {
18966
18967
18968             }
18969         }
18970     },
18971
18972     clickValidator: function(e) {
18973         var target = e.getTarget();
18974         return ( this.isValidHandleChild(target) &&
18975                     (this.id == this.handleElId ||
18976                         this.DDM.handleWasClicked(target, this.id)) );
18977     },
18978
18979     /**
18980      * Allows you to specify a tag name that should not start a drag operation
18981      * when clicked.  This is designed to facilitate embedding links within a
18982      * drag handle that do something other than start the drag.
18983      * @method addInvalidHandleType
18984      * @param {string} tagName the type of element to exclude
18985      */
18986     addInvalidHandleType: function(tagName) {
18987         var type = tagName.toUpperCase();
18988         this.invalidHandleTypes[type] = type;
18989     },
18990
18991     /**
18992      * Lets you to specify an element id for a child of a drag handle
18993      * that should not initiate a drag
18994      * @method addInvalidHandleId
18995      * @param {string} id the element id of the element you wish to ignore
18996      */
18997     addInvalidHandleId: function(id) {
18998         if (typeof id !== "string") {
18999             id = Roo.id(id);
19000         }
19001         this.invalidHandleIds[id] = id;
19002     },
19003
19004     /**
19005      * Lets you specify a css class of elements that will not initiate a drag
19006      * @method addInvalidHandleClass
19007      * @param {string} cssClass the class of the elements you wish to ignore
19008      */
19009     addInvalidHandleClass: function(cssClass) {
19010         this.invalidHandleClasses.push(cssClass);
19011     },
19012
19013     /**
19014      * Unsets an excluded tag name set by addInvalidHandleType
19015      * @method removeInvalidHandleType
19016      * @param {string} tagName the type of element to unexclude
19017      */
19018     removeInvalidHandleType: function(tagName) {
19019         var type = tagName.toUpperCase();
19020         // this.invalidHandleTypes[type] = null;
19021         delete this.invalidHandleTypes[type];
19022     },
19023
19024     /**
19025      * Unsets an invalid handle id
19026      * @method removeInvalidHandleId
19027      * @param {string} id the id of the element to re-enable
19028      */
19029     removeInvalidHandleId: function(id) {
19030         if (typeof id !== "string") {
19031             id = Roo.id(id);
19032         }
19033         delete this.invalidHandleIds[id];
19034     },
19035
19036     /**
19037      * Unsets an invalid css class
19038      * @method removeInvalidHandleClass
19039      * @param {string} cssClass the class of the element(s) you wish to
19040      * re-enable
19041      */
19042     removeInvalidHandleClass: function(cssClass) {
19043         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19044             if (this.invalidHandleClasses[i] == cssClass) {
19045                 delete this.invalidHandleClasses[i];
19046             }
19047         }
19048     },
19049
19050     /**
19051      * Checks the tag exclusion list to see if this click should be ignored
19052      * @method isValidHandleChild
19053      * @param {HTMLElement} node the HTMLElement to evaluate
19054      * @return {boolean} true if this is a valid tag type, false if not
19055      */
19056     isValidHandleChild: function(node) {
19057
19058         var valid = true;
19059         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19060         var nodeName;
19061         try {
19062             nodeName = node.nodeName.toUpperCase();
19063         } catch(e) {
19064             nodeName = node.nodeName;
19065         }
19066         valid = valid && !this.invalidHandleTypes[nodeName];
19067         valid = valid && !this.invalidHandleIds[node.id];
19068
19069         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19070             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19071         }
19072
19073
19074         return valid;
19075
19076     },
19077
19078     /**
19079      * Create the array of horizontal tick marks if an interval was specified
19080      * in setXConstraint().
19081      * @method setXTicks
19082      * @private
19083      */
19084     setXTicks: function(iStartX, iTickSize) {
19085         this.xTicks = [];
19086         this.xTickSize = iTickSize;
19087
19088         var tickMap = {};
19089
19090         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19091             if (!tickMap[i]) {
19092                 this.xTicks[this.xTicks.length] = i;
19093                 tickMap[i] = true;
19094             }
19095         }
19096
19097         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19098             if (!tickMap[i]) {
19099                 this.xTicks[this.xTicks.length] = i;
19100                 tickMap[i] = true;
19101             }
19102         }
19103
19104         this.xTicks.sort(this.DDM.numericSort) ;
19105     },
19106
19107     /**
19108      * Create the array of vertical tick marks if an interval was specified in
19109      * setYConstraint().
19110      * @method setYTicks
19111      * @private
19112      */
19113     setYTicks: function(iStartY, iTickSize) {
19114         this.yTicks = [];
19115         this.yTickSize = iTickSize;
19116
19117         var tickMap = {};
19118
19119         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19120             if (!tickMap[i]) {
19121                 this.yTicks[this.yTicks.length] = i;
19122                 tickMap[i] = true;
19123             }
19124         }
19125
19126         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19127             if (!tickMap[i]) {
19128                 this.yTicks[this.yTicks.length] = i;
19129                 tickMap[i] = true;
19130             }
19131         }
19132
19133         this.yTicks.sort(this.DDM.numericSort) ;
19134     },
19135
19136     /**
19137      * By default, the element can be dragged any place on the screen.  Use
19138      * this method to limit the horizontal travel of the element.  Pass in
19139      * 0,0 for the parameters if you want to lock the drag to the y axis.
19140      * @method setXConstraint
19141      * @param {int} iLeft the number of pixels the element can move to the left
19142      * @param {int} iRight the number of pixels the element can move to the
19143      * right
19144      * @param {int} iTickSize optional parameter for specifying that the
19145      * element
19146      * should move iTickSize pixels at a time.
19147      */
19148     setXConstraint: function(iLeft, iRight, iTickSize) {
19149         this.leftConstraint = iLeft;
19150         this.rightConstraint = iRight;
19151
19152         this.minX = this.initPageX - iLeft;
19153         this.maxX = this.initPageX + iRight;
19154         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19155
19156         this.constrainX = true;
19157     },
19158
19159     /**
19160      * Clears any constraints applied to this instance.  Also clears ticks
19161      * since they can't exist independent of a constraint at this time.
19162      * @method clearConstraints
19163      */
19164     clearConstraints: function() {
19165         this.constrainX = false;
19166         this.constrainY = false;
19167         this.clearTicks();
19168     },
19169
19170     /**
19171      * Clears any tick interval defined for this instance
19172      * @method clearTicks
19173      */
19174     clearTicks: function() {
19175         this.xTicks = null;
19176         this.yTicks = null;
19177         this.xTickSize = 0;
19178         this.yTickSize = 0;
19179     },
19180
19181     /**
19182      * By default, the element can be dragged any place on the screen.  Set
19183      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19184      * parameters if you want to lock the drag to the x axis.
19185      * @method setYConstraint
19186      * @param {int} iUp the number of pixels the element can move up
19187      * @param {int} iDown the number of pixels the element can move down
19188      * @param {int} iTickSize optional parameter for specifying that the
19189      * element should move iTickSize pixels at a time.
19190      */
19191     setYConstraint: function(iUp, iDown, iTickSize) {
19192         this.topConstraint = iUp;
19193         this.bottomConstraint = iDown;
19194
19195         this.minY = this.initPageY - iUp;
19196         this.maxY = this.initPageY + iDown;
19197         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19198
19199         this.constrainY = true;
19200
19201     },
19202
19203     /**
19204      * resetConstraints must be called if you manually reposition a dd element.
19205      * @method resetConstraints
19206      * @param {boolean} maintainOffset
19207      */
19208     resetConstraints: function() {
19209
19210
19211         // Maintain offsets if necessary
19212         if (this.initPageX || this.initPageX === 0) {
19213             // figure out how much this thing has moved
19214             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19215             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19216
19217             this.setInitPosition(dx, dy);
19218
19219         // This is the first time we have detected the element's position
19220         } else {
19221             this.setInitPosition();
19222         }
19223
19224         if (this.constrainX) {
19225             this.setXConstraint( this.leftConstraint,
19226                                  this.rightConstraint,
19227                                  this.xTickSize        );
19228         }
19229
19230         if (this.constrainY) {
19231             this.setYConstraint( this.topConstraint,
19232                                  this.bottomConstraint,
19233                                  this.yTickSize         );
19234         }
19235     },
19236
19237     /**
19238      * Normally the drag element is moved pixel by pixel, but we can specify
19239      * that it move a number of pixels at a time.  This method resolves the
19240      * location when we have it set up like this.
19241      * @method getTick
19242      * @param {int} val where we want to place the object
19243      * @param {int[]} tickArray sorted array of valid points
19244      * @return {int} the closest tick
19245      * @private
19246      */
19247     getTick: function(val, tickArray) {
19248
19249         if (!tickArray) {
19250             // If tick interval is not defined, it is effectively 1 pixel,
19251             // so we return the value passed to us.
19252             return val;
19253         } else if (tickArray[0] >= val) {
19254             // The value is lower than the first tick, so we return the first
19255             // tick.
19256             return tickArray[0];
19257         } else {
19258             for (var i=0, len=tickArray.length; i<len; ++i) {
19259                 var next = i + 1;
19260                 if (tickArray[next] && tickArray[next] >= val) {
19261                     var diff1 = val - tickArray[i];
19262                     var diff2 = tickArray[next] - val;
19263                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19264                 }
19265             }
19266
19267             // The value is larger than the last tick, so we return the last
19268             // tick.
19269             return tickArray[tickArray.length - 1];
19270         }
19271     },
19272
19273     /**
19274      * toString method
19275      * @method toString
19276      * @return {string} string representation of the dd obj
19277      */
19278     toString: function() {
19279         return ("DragDrop " + this.id);
19280     }
19281
19282 });
19283
19284 })();
19285 /*
19286  * Based on:
19287  * Ext JS Library 1.1.1
19288  * Copyright(c) 2006-2007, Ext JS, LLC.
19289  *
19290  * Originally Released Under LGPL - original licence link has changed is not relivant.
19291  *
19292  * Fork - LGPL
19293  * <script type="text/javascript">
19294  */
19295
19296
19297 /**
19298  * The drag and drop utility provides a framework for building drag and drop
19299  * applications.  In addition to enabling drag and drop for specific elements,
19300  * the drag and drop elements are tracked by the manager class, and the
19301  * interactions between the various elements are tracked during the drag and
19302  * the implementing code is notified about these important moments.
19303  */
19304
19305 // Only load the library once.  Rewriting the manager class would orphan
19306 // existing drag and drop instances.
19307 if (!Roo.dd.DragDropMgr) {
19308
19309 /**
19310  * @class Roo.dd.DragDropMgr
19311  * DragDropMgr is a singleton that tracks the element interaction for
19312  * all DragDrop items in the window.  Generally, you will not call
19313  * this class directly, but it does have helper methods that could
19314  * be useful in your DragDrop implementations.
19315  * @singleton
19316  */
19317 Roo.dd.DragDropMgr = function() {
19318
19319     var Event = Roo.EventManager;
19320
19321     return {
19322
19323         /**
19324          * Two dimensional Array of registered DragDrop objects.  The first
19325          * dimension is the DragDrop item group, the second the DragDrop
19326          * object.
19327          * @property ids
19328          * @type {string: string}
19329          * @private
19330          * @static
19331          */
19332         ids: {},
19333
19334         /**
19335          * Array of element ids defined as drag handles.  Used to determine
19336          * if the element that generated the mousedown event is actually the
19337          * handle and not the html element itself.
19338          * @property handleIds
19339          * @type {string: string}
19340          * @private
19341          * @static
19342          */
19343         handleIds: {},
19344
19345         /**
19346          * the DragDrop object that is currently being dragged
19347          * @property dragCurrent
19348          * @type DragDrop
19349          * @private
19350          * @static
19351          **/
19352         dragCurrent: null,
19353
19354         /**
19355          * the DragDrop object(s) that are being hovered over
19356          * @property dragOvers
19357          * @type Array
19358          * @private
19359          * @static
19360          */
19361         dragOvers: {},
19362
19363         /**
19364          * the X distance between the cursor and the object being dragged
19365          * @property deltaX
19366          * @type int
19367          * @private
19368          * @static
19369          */
19370         deltaX: 0,
19371
19372         /**
19373          * the Y distance between the cursor and the object being dragged
19374          * @property deltaY
19375          * @type int
19376          * @private
19377          * @static
19378          */
19379         deltaY: 0,
19380
19381         /**
19382          * Flag to determine if we should prevent the default behavior of the
19383          * events we define. By default this is true, but this can be set to
19384          * false if you need the default behavior (not recommended)
19385          * @property preventDefault
19386          * @type boolean
19387          * @static
19388          */
19389         preventDefault: true,
19390
19391         /**
19392          * Flag to determine if we should stop the propagation of the events
19393          * we generate. This is true by default but you may want to set it to
19394          * false if the html element contains other features that require the
19395          * mouse click.
19396          * @property stopPropagation
19397          * @type boolean
19398          * @static
19399          */
19400         stopPropagation: true,
19401
19402         /**
19403          * Internal flag that is set to true when drag and drop has been
19404          * intialized
19405          * @property initialized
19406          * @private
19407          * @static
19408          */
19409         initalized: false,
19410
19411         /**
19412          * All drag and drop can be disabled.
19413          * @property locked
19414          * @private
19415          * @static
19416          */
19417         locked: false,
19418
19419         /**
19420          * Called the first time an element is registered.
19421          * @method init
19422          * @private
19423          * @static
19424          */
19425         init: function() {
19426             this.initialized = true;
19427         },
19428
19429         /**
19430          * In point mode, drag and drop interaction is defined by the
19431          * location of the cursor during the drag/drop
19432          * @property POINT
19433          * @type int
19434          * @static
19435          */
19436         POINT: 0,
19437
19438         /**
19439          * In intersect mode, drag and drop interactio nis defined by the
19440          * overlap of two or more drag and drop objects.
19441          * @property INTERSECT
19442          * @type int
19443          * @static
19444          */
19445         INTERSECT: 1,
19446
19447         /**
19448          * The current drag and drop mode.  Default: POINT
19449          * @property mode
19450          * @type int
19451          * @static
19452          */
19453         mode: 0,
19454
19455         /**
19456          * Runs method on all drag and drop objects
19457          * @method _execOnAll
19458          * @private
19459          * @static
19460          */
19461         _execOnAll: function(sMethod, args) {
19462             for (var i in this.ids) {
19463                 for (var j in this.ids[i]) {
19464                     var oDD = this.ids[i][j];
19465                     if (! this.isTypeOfDD(oDD)) {
19466                         continue;
19467                     }
19468                     oDD[sMethod].apply(oDD, args);
19469                 }
19470             }
19471         },
19472
19473         /**
19474          * Drag and drop initialization.  Sets up the global event handlers
19475          * @method _onLoad
19476          * @private
19477          * @static
19478          */
19479         _onLoad: function() {
19480
19481             this.init();
19482
19483             if (!Roo.isTouch) {
19484                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19485                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19486             }
19487             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19488             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19489             
19490             Event.on(window,   "unload",    this._onUnload, this, true);
19491             Event.on(window,   "resize",    this._onResize, this, true);
19492             // Event.on(window,   "mouseout",    this._test);
19493
19494         },
19495
19496         /**
19497          * Reset constraints on all drag and drop objs
19498          * @method _onResize
19499          * @private
19500          * @static
19501          */
19502         _onResize: function(e) {
19503             this._execOnAll("resetConstraints", []);
19504         },
19505
19506         /**
19507          * Lock all drag and drop functionality
19508          * @method lock
19509          * @static
19510          */
19511         lock: function() { this.locked = true; },
19512
19513         /**
19514          * Unlock all drag and drop functionality
19515          * @method unlock
19516          * @static
19517          */
19518         unlock: function() { this.locked = false; },
19519
19520         /**
19521          * Is drag and drop locked?
19522          * @method isLocked
19523          * @return {boolean} True if drag and drop is locked, false otherwise.
19524          * @static
19525          */
19526         isLocked: function() { return this.locked; },
19527
19528         /**
19529          * Location cache that is set for all drag drop objects when a drag is
19530          * initiated, cleared when the drag is finished.
19531          * @property locationCache
19532          * @private
19533          * @static
19534          */
19535         locationCache: {},
19536
19537         /**
19538          * Set useCache to false if you want to force object the lookup of each
19539          * drag and drop linked element constantly during a drag.
19540          * @property useCache
19541          * @type boolean
19542          * @static
19543          */
19544         useCache: true,
19545
19546         /**
19547          * The number of pixels that the mouse needs to move after the
19548          * mousedown before the drag is initiated.  Default=3;
19549          * @property clickPixelThresh
19550          * @type int
19551          * @static
19552          */
19553         clickPixelThresh: 3,
19554
19555         /**
19556          * The number of milliseconds after the mousedown event to initiate the
19557          * drag if we don't get a mouseup event. Default=1000
19558          * @property clickTimeThresh
19559          * @type int
19560          * @static
19561          */
19562         clickTimeThresh: 350,
19563
19564         /**
19565          * Flag that indicates that either the drag pixel threshold or the
19566          * mousdown time threshold has been met
19567          * @property dragThreshMet
19568          * @type boolean
19569          * @private
19570          * @static
19571          */
19572         dragThreshMet: false,
19573
19574         /**
19575          * Timeout used for the click time threshold
19576          * @property clickTimeout
19577          * @type Object
19578          * @private
19579          * @static
19580          */
19581         clickTimeout: null,
19582
19583         /**
19584          * The X position of the mousedown event stored for later use when a
19585          * drag threshold is met.
19586          * @property startX
19587          * @type int
19588          * @private
19589          * @static
19590          */
19591         startX: 0,
19592
19593         /**
19594          * The Y position of the mousedown event stored for later use when a
19595          * drag threshold is met.
19596          * @property startY
19597          * @type int
19598          * @private
19599          * @static
19600          */
19601         startY: 0,
19602
19603         /**
19604          * Each DragDrop instance must be registered with the DragDropMgr.
19605          * This is executed in DragDrop.init()
19606          * @method regDragDrop
19607          * @param {DragDrop} oDD the DragDrop object to register
19608          * @param {String} sGroup the name of the group this element belongs to
19609          * @static
19610          */
19611         regDragDrop: function(oDD, sGroup) {
19612             if (!this.initialized) { this.init(); }
19613
19614             if (!this.ids[sGroup]) {
19615                 this.ids[sGroup] = {};
19616             }
19617             this.ids[sGroup][oDD.id] = oDD;
19618         },
19619
19620         /**
19621          * Removes the supplied dd instance from the supplied group. Executed
19622          * by DragDrop.removeFromGroup, so don't call this function directly.
19623          * @method removeDDFromGroup
19624          * @private
19625          * @static
19626          */
19627         removeDDFromGroup: function(oDD, sGroup) {
19628             if (!this.ids[sGroup]) {
19629                 this.ids[sGroup] = {};
19630             }
19631
19632             var obj = this.ids[sGroup];
19633             if (obj && obj[oDD.id]) {
19634                 delete obj[oDD.id];
19635             }
19636         },
19637
19638         /**
19639          * Unregisters a drag and drop item.  This is executed in
19640          * DragDrop.unreg, use that method instead of calling this directly.
19641          * @method _remove
19642          * @private
19643          * @static
19644          */
19645         _remove: function(oDD) {
19646             for (var g in oDD.groups) {
19647                 if (g && this.ids[g][oDD.id]) {
19648                     delete this.ids[g][oDD.id];
19649                 }
19650             }
19651             delete this.handleIds[oDD.id];
19652         },
19653
19654         /**
19655          * Each DragDrop handle element must be registered.  This is done
19656          * automatically when executing DragDrop.setHandleElId()
19657          * @method regHandle
19658          * @param {String} sDDId the DragDrop id this element is a handle for
19659          * @param {String} sHandleId the id of the element that is the drag
19660          * handle
19661          * @static
19662          */
19663         regHandle: function(sDDId, sHandleId) {
19664             if (!this.handleIds[sDDId]) {
19665                 this.handleIds[sDDId] = {};
19666             }
19667             this.handleIds[sDDId][sHandleId] = sHandleId;
19668         },
19669
19670         /**
19671          * Utility function to determine if a given element has been
19672          * registered as a drag drop item.
19673          * @method isDragDrop
19674          * @param {String} id the element id to check
19675          * @return {boolean} true if this element is a DragDrop item,
19676          * false otherwise
19677          * @static
19678          */
19679         isDragDrop: function(id) {
19680             return ( this.getDDById(id) ) ? true : false;
19681         },
19682
19683         /**
19684          * Returns the drag and drop instances that are in all groups the
19685          * passed in instance belongs to.
19686          * @method getRelated
19687          * @param {DragDrop} p_oDD the obj to get related data for
19688          * @param {boolean} bTargetsOnly if true, only return targetable objs
19689          * @return {DragDrop[]} the related instances
19690          * @static
19691          */
19692         getRelated: function(p_oDD, bTargetsOnly) {
19693             var oDDs = [];
19694             for (var i in p_oDD.groups) {
19695                 for (j in this.ids[i]) {
19696                     var dd = this.ids[i][j];
19697                     if (! this.isTypeOfDD(dd)) {
19698                         continue;
19699                     }
19700                     if (!bTargetsOnly || dd.isTarget) {
19701                         oDDs[oDDs.length] = dd;
19702                     }
19703                 }
19704             }
19705
19706             return oDDs;
19707         },
19708
19709         /**
19710          * Returns true if the specified dd target is a legal target for
19711          * the specifice drag obj
19712          * @method isLegalTarget
19713          * @param {DragDrop} the drag obj
19714          * @param {DragDrop} the target
19715          * @return {boolean} true if the target is a legal target for the
19716          * dd obj
19717          * @static
19718          */
19719         isLegalTarget: function (oDD, oTargetDD) {
19720             var targets = this.getRelated(oDD, true);
19721             for (var i=0, len=targets.length;i<len;++i) {
19722                 if (targets[i].id == oTargetDD.id) {
19723                     return true;
19724                 }
19725             }
19726
19727             return false;
19728         },
19729
19730         /**
19731          * My goal is to be able to transparently determine if an object is
19732          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19733          * returns "object", oDD.constructor.toString() always returns
19734          * "DragDrop" and not the name of the subclass.  So for now it just
19735          * evaluates a well-known variable in DragDrop.
19736          * @method isTypeOfDD
19737          * @param {Object} the object to evaluate
19738          * @return {boolean} true if typeof oDD = DragDrop
19739          * @static
19740          */
19741         isTypeOfDD: function (oDD) {
19742             return (oDD && oDD.__ygDragDrop);
19743         },
19744
19745         /**
19746          * Utility function to determine if a given element has been
19747          * registered as a drag drop handle for the given Drag Drop object.
19748          * @method isHandle
19749          * @param {String} id the element id to check
19750          * @return {boolean} true if this element is a DragDrop handle, false
19751          * otherwise
19752          * @static
19753          */
19754         isHandle: function(sDDId, sHandleId) {
19755             return ( this.handleIds[sDDId] &&
19756                             this.handleIds[sDDId][sHandleId] );
19757         },
19758
19759         /**
19760          * Returns the DragDrop instance for a given id
19761          * @method getDDById
19762          * @param {String} id the id of the DragDrop object
19763          * @return {DragDrop} the drag drop object, null if it is not found
19764          * @static
19765          */
19766         getDDById: function(id) {
19767             for (var i in this.ids) {
19768                 if (this.ids[i][id]) {
19769                     return this.ids[i][id];
19770                 }
19771             }
19772             return null;
19773         },
19774
19775         /**
19776          * Fired after a registered DragDrop object gets the mousedown event.
19777          * Sets up the events required to track the object being dragged
19778          * @method handleMouseDown
19779          * @param {Event} e the event
19780          * @param oDD the DragDrop object being dragged
19781          * @private
19782          * @static
19783          */
19784         handleMouseDown: function(e, oDD) {
19785             if(Roo.QuickTips){
19786                 Roo.QuickTips.disable();
19787             }
19788             this.currentTarget = e.getTarget();
19789
19790             this.dragCurrent = oDD;
19791
19792             var el = oDD.getEl();
19793
19794             // track start position
19795             this.startX = e.getPageX();
19796             this.startY = e.getPageY();
19797
19798             this.deltaX = this.startX - el.offsetLeft;
19799             this.deltaY = this.startY - el.offsetTop;
19800
19801             this.dragThreshMet = false;
19802
19803             this.clickTimeout = setTimeout(
19804                     function() {
19805                         var DDM = Roo.dd.DDM;
19806                         DDM.startDrag(DDM.startX, DDM.startY);
19807                     },
19808                     this.clickTimeThresh );
19809         },
19810
19811         /**
19812          * Fired when either the drag pixel threshol or the mousedown hold
19813          * time threshold has been met.
19814          * @method startDrag
19815          * @param x {int} the X position of the original mousedown
19816          * @param y {int} the Y position of the original mousedown
19817          * @static
19818          */
19819         startDrag: function(x, y) {
19820             clearTimeout(this.clickTimeout);
19821             if (this.dragCurrent) {
19822                 this.dragCurrent.b4StartDrag(x, y);
19823                 this.dragCurrent.startDrag(x, y);
19824             }
19825             this.dragThreshMet = true;
19826         },
19827
19828         /**
19829          * Internal function to handle the mouseup event.  Will be invoked
19830          * from the context of the document.
19831          * @method handleMouseUp
19832          * @param {Event} e the event
19833          * @private
19834          * @static
19835          */
19836         handleMouseUp: function(e) {
19837
19838             if(Roo.QuickTips){
19839                 Roo.QuickTips.enable();
19840             }
19841             if (! this.dragCurrent) {
19842                 return;
19843             }
19844
19845             clearTimeout(this.clickTimeout);
19846
19847             if (this.dragThreshMet) {
19848                 this.fireEvents(e, true);
19849             } else {
19850             }
19851
19852             this.stopDrag(e);
19853
19854             this.stopEvent(e);
19855         },
19856
19857         /**
19858          * Utility to stop event propagation and event default, if these
19859          * features are turned on.
19860          * @method stopEvent
19861          * @param {Event} e the event as returned by this.getEvent()
19862          * @static
19863          */
19864         stopEvent: function(e){
19865             if(this.stopPropagation) {
19866                 e.stopPropagation();
19867             }
19868
19869             if (this.preventDefault) {
19870                 e.preventDefault();
19871             }
19872         },
19873
19874         /**
19875          * Internal function to clean up event handlers after the drag
19876          * operation is complete
19877          * @method stopDrag
19878          * @param {Event} e the event
19879          * @private
19880          * @static
19881          */
19882         stopDrag: function(e) {
19883             // Fire the drag end event for the item that was dragged
19884             if (this.dragCurrent) {
19885                 if (this.dragThreshMet) {
19886                     this.dragCurrent.b4EndDrag(e);
19887                     this.dragCurrent.endDrag(e);
19888                 }
19889
19890                 this.dragCurrent.onMouseUp(e);
19891             }
19892
19893             this.dragCurrent = null;
19894             this.dragOvers = {};
19895         },
19896
19897         /**
19898          * Internal function to handle the mousemove event.  Will be invoked
19899          * from the context of the html element.
19900          *
19901          * @TODO figure out what we can do about mouse events lost when the
19902          * user drags objects beyond the window boundary.  Currently we can
19903          * detect this in internet explorer by verifying that the mouse is
19904          * down during the mousemove event.  Firefox doesn't give us the
19905          * button state on the mousemove event.
19906          * @method handleMouseMove
19907          * @param {Event} e the event
19908          * @private
19909          * @static
19910          */
19911         handleMouseMove: function(e) {
19912             if (! this.dragCurrent) {
19913                 return true;
19914             }
19915
19916             // var button = e.which || e.button;
19917
19918             // check for IE mouseup outside of page boundary
19919             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19920                 this.stopEvent(e);
19921                 return this.handleMouseUp(e);
19922             }
19923
19924             if (!this.dragThreshMet) {
19925                 var diffX = Math.abs(this.startX - e.getPageX());
19926                 var diffY = Math.abs(this.startY - e.getPageY());
19927                 if (diffX > this.clickPixelThresh ||
19928                             diffY > this.clickPixelThresh) {
19929                     this.startDrag(this.startX, this.startY);
19930                 }
19931             }
19932
19933             if (this.dragThreshMet) {
19934                 this.dragCurrent.b4Drag(e);
19935                 this.dragCurrent.onDrag(e);
19936                 if(!this.dragCurrent.moveOnly){
19937                     this.fireEvents(e, false);
19938                 }
19939             }
19940
19941             this.stopEvent(e);
19942
19943             return true;
19944         },
19945
19946         /**
19947          * Iterates over all of the DragDrop elements to find ones we are
19948          * hovering over or dropping on
19949          * @method fireEvents
19950          * @param {Event} e the event
19951          * @param {boolean} isDrop is this a drop op or a mouseover op?
19952          * @private
19953          * @static
19954          */
19955         fireEvents: function(e, isDrop) {
19956             var dc = this.dragCurrent;
19957
19958             // If the user did the mouse up outside of the window, we could
19959             // get here even though we have ended the drag.
19960             if (!dc || dc.isLocked()) {
19961                 return;
19962             }
19963
19964             var pt = e.getPoint();
19965
19966             // cache the previous dragOver array
19967             var oldOvers = [];
19968
19969             var outEvts   = [];
19970             var overEvts  = [];
19971             var dropEvts  = [];
19972             var enterEvts = [];
19973
19974             // Check to see if the object(s) we were hovering over is no longer
19975             // being hovered over so we can fire the onDragOut event
19976             for (var i in this.dragOvers) {
19977
19978                 var ddo = this.dragOvers[i];
19979
19980                 if (! this.isTypeOfDD(ddo)) {
19981                     continue;
19982                 }
19983
19984                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19985                     outEvts.push( ddo );
19986                 }
19987
19988                 oldOvers[i] = true;
19989                 delete this.dragOvers[i];
19990             }
19991
19992             for (var sGroup in dc.groups) {
19993
19994                 if ("string" != typeof sGroup) {
19995                     continue;
19996                 }
19997
19998                 for (i in this.ids[sGroup]) {
19999                     var oDD = this.ids[sGroup][i];
20000                     if (! this.isTypeOfDD(oDD)) {
20001                         continue;
20002                     }
20003
20004                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20005                         if (this.isOverTarget(pt, oDD, this.mode)) {
20006                             // look for drop interactions
20007                             if (isDrop) {
20008                                 dropEvts.push( oDD );
20009                             // look for drag enter and drag over interactions
20010                             } else {
20011
20012                                 // initial drag over: dragEnter fires
20013                                 if (!oldOvers[oDD.id]) {
20014                                     enterEvts.push( oDD );
20015                                 // subsequent drag overs: dragOver fires
20016                                 } else {
20017                                     overEvts.push( oDD );
20018                                 }
20019
20020                                 this.dragOvers[oDD.id] = oDD;
20021                             }
20022                         }
20023                     }
20024                 }
20025             }
20026
20027             if (this.mode) {
20028                 if (outEvts.length) {
20029                     dc.b4DragOut(e, outEvts);
20030                     dc.onDragOut(e, outEvts);
20031                 }
20032
20033                 if (enterEvts.length) {
20034                     dc.onDragEnter(e, enterEvts);
20035                 }
20036
20037                 if (overEvts.length) {
20038                     dc.b4DragOver(e, overEvts);
20039                     dc.onDragOver(e, overEvts);
20040                 }
20041
20042                 if (dropEvts.length) {
20043                     dc.b4DragDrop(e, dropEvts);
20044                     dc.onDragDrop(e, dropEvts);
20045                 }
20046
20047             } else {
20048                 // fire dragout events
20049                 var len = 0;
20050                 for (i=0, len=outEvts.length; i<len; ++i) {
20051                     dc.b4DragOut(e, outEvts[i].id);
20052                     dc.onDragOut(e, outEvts[i].id);
20053                 }
20054
20055                 // fire enter events
20056                 for (i=0,len=enterEvts.length; i<len; ++i) {
20057                     // dc.b4DragEnter(e, oDD.id);
20058                     dc.onDragEnter(e, enterEvts[i].id);
20059                 }
20060
20061                 // fire over events
20062                 for (i=0,len=overEvts.length; i<len; ++i) {
20063                     dc.b4DragOver(e, overEvts[i].id);
20064                     dc.onDragOver(e, overEvts[i].id);
20065                 }
20066
20067                 // fire drop events
20068                 for (i=0, len=dropEvts.length; i<len; ++i) {
20069                     dc.b4DragDrop(e, dropEvts[i].id);
20070                     dc.onDragDrop(e, dropEvts[i].id);
20071                 }
20072
20073             }
20074
20075             // notify about a drop that did not find a target
20076             if (isDrop && !dropEvts.length) {
20077                 dc.onInvalidDrop(e);
20078             }
20079
20080         },
20081
20082         /**
20083          * Helper function for getting the best match from the list of drag
20084          * and drop objects returned by the drag and drop events when we are
20085          * in INTERSECT mode.  It returns either the first object that the
20086          * cursor is over, or the object that has the greatest overlap with
20087          * the dragged element.
20088          * @method getBestMatch
20089          * @param  {DragDrop[]} dds The array of drag and drop objects
20090          * targeted
20091          * @return {DragDrop}       The best single match
20092          * @static
20093          */
20094         getBestMatch: function(dds) {
20095             var winner = null;
20096             // Return null if the input is not what we expect
20097             //if (!dds || !dds.length || dds.length == 0) {
20098                // winner = null;
20099             // If there is only one item, it wins
20100             //} else if (dds.length == 1) {
20101
20102             var len = dds.length;
20103
20104             if (len == 1) {
20105                 winner = dds[0];
20106             } else {
20107                 // Loop through the targeted items
20108                 for (var i=0; i<len; ++i) {
20109                     var dd = dds[i];
20110                     // If the cursor is over the object, it wins.  If the
20111                     // cursor is over multiple matches, the first one we come
20112                     // to wins.
20113                     if (dd.cursorIsOver) {
20114                         winner = dd;
20115                         break;
20116                     // Otherwise the object with the most overlap wins
20117                     } else {
20118                         if (!winner ||
20119                             winner.overlap.getArea() < dd.overlap.getArea()) {
20120                             winner = dd;
20121                         }
20122                     }
20123                 }
20124             }
20125
20126             return winner;
20127         },
20128
20129         /**
20130          * Refreshes the cache of the top-left and bottom-right points of the
20131          * drag and drop objects in the specified group(s).  This is in the
20132          * format that is stored in the drag and drop instance, so typical
20133          * usage is:
20134          * <code>
20135          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20136          * </code>
20137          * Alternatively:
20138          * <code>
20139          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20140          * </code>
20141          * @TODO this really should be an indexed array.  Alternatively this
20142          * method could accept both.
20143          * @method refreshCache
20144          * @param {Object} groups an associative array of groups to refresh
20145          * @static
20146          */
20147         refreshCache: function(groups) {
20148             for (var sGroup in groups) {
20149                 if ("string" != typeof sGroup) {
20150                     continue;
20151                 }
20152                 for (var i in this.ids[sGroup]) {
20153                     var oDD = this.ids[sGroup][i];
20154
20155                     if (this.isTypeOfDD(oDD)) {
20156                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20157                         var loc = this.getLocation(oDD);
20158                         if (loc) {
20159                             this.locationCache[oDD.id] = loc;
20160                         } else {
20161                             delete this.locationCache[oDD.id];
20162                             // this will unregister the drag and drop object if
20163                             // the element is not in a usable state
20164                             // oDD.unreg();
20165                         }
20166                     }
20167                 }
20168             }
20169         },
20170
20171         /**
20172          * This checks to make sure an element exists and is in the DOM.  The
20173          * main purpose is to handle cases where innerHTML is used to remove
20174          * drag and drop objects from the DOM.  IE provides an 'unspecified
20175          * error' when trying to access the offsetParent of such an element
20176          * @method verifyEl
20177          * @param {HTMLElement} el the element to check
20178          * @return {boolean} true if the element looks usable
20179          * @static
20180          */
20181         verifyEl: function(el) {
20182             if (el) {
20183                 var parent;
20184                 if(Roo.isIE){
20185                     try{
20186                         parent = el.offsetParent;
20187                     }catch(e){}
20188                 }else{
20189                     parent = el.offsetParent;
20190                 }
20191                 if (parent) {
20192                     return true;
20193                 }
20194             }
20195
20196             return false;
20197         },
20198
20199         /**
20200          * Returns a Region object containing the drag and drop element's position
20201          * and size, including the padding configured for it
20202          * @method getLocation
20203          * @param {DragDrop} oDD the drag and drop object to get the
20204          *                       location for
20205          * @return {Roo.lib.Region} a Region object representing the total area
20206          *                             the element occupies, including any padding
20207          *                             the instance is configured for.
20208          * @static
20209          */
20210         getLocation: function(oDD) {
20211             if (! this.isTypeOfDD(oDD)) {
20212                 return null;
20213             }
20214
20215             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20216
20217             try {
20218                 pos= Roo.lib.Dom.getXY(el);
20219             } catch (e) { }
20220
20221             if (!pos) {
20222                 return null;
20223             }
20224
20225             x1 = pos[0];
20226             x2 = x1 + el.offsetWidth;
20227             y1 = pos[1];
20228             y2 = y1 + el.offsetHeight;
20229
20230             t = y1 - oDD.padding[0];
20231             r = x2 + oDD.padding[1];
20232             b = y2 + oDD.padding[2];
20233             l = x1 - oDD.padding[3];
20234
20235             return new Roo.lib.Region( t, r, b, l );
20236         },
20237
20238         /**
20239          * Checks the cursor location to see if it over the target
20240          * @method isOverTarget
20241          * @param {Roo.lib.Point} pt The point to evaluate
20242          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20243          * @return {boolean} true if the mouse is over the target
20244          * @private
20245          * @static
20246          */
20247         isOverTarget: function(pt, oTarget, intersect) {
20248             // use cache if available
20249             var loc = this.locationCache[oTarget.id];
20250             if (!loc || !this.useCache) {
20251                 loc = this.getLocation(oTarget);
20252                 this.locationCache[oTarget.id] = loc;
20253
20254             }
20255
20256             if (!loc) {
20257                 return false;
20258             }
20259
20260             oTarget.cursorIsOver = loc.contains( pt );
20261
20262             // DragDrop is using this as a sanity check for the initial mousedown
20263             // in this case we are done.  In POINT mode, if the drag obj has no
20264             // contraints, we are also done. Otherwise we need to evaluate the
20265             // location of the target as related to the actual location of the
20266             // dragged element.
20267             var dc = this.dragCurrent;
20268             if (!dc || !dc.getTargetCoord ||
20269                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20270                 return oTarget.cursorIsOver;
20271             }
20272
20273             oTarget.overlap = null;
20274
20275             // Get the current location of the drag element, this is the
20276             // location of the mouse event less the delta that represents
20277             // where the original mousedown happened on the element.  We
20278             // need to consider constraints and ticks as well.
20279             var pos = dc.getTargetCoord(pt.x, pt.y);
20280
20281             var el = dc.getDragEl();
20282             var curRegion = new Roo.lib.Region( pos.y,
20283                                                    pos.x + el.offsetWidth,
20284                                                    pos.y + el.offsetHeight,
20285                                                    pos.x );
20286
20287             var overlap = curRegion.intersect(loc);
20288
20289             if (overlap) {
20290                 oTarget.overlap = overlap;
20291                 return (intersect) ? true : oTarget.cursorIsOver;
20292             } else {
20293                 return false;
20294             }
20295         },
20296
20297         /**
20298          * unload event handler
20299          * @method _onUnload
20300          * @private
20301          * @static
20302          */
20303         _onUnload: function(e, me) {
20304             Roo.dd.DragDropMgr.unregAll();
20305         },
20306
20307         /**
20308          * Cleans up the drag and drop events and objects.
20309          * @method unregAll
20310          * @private
20311          * @static
20312          */
20313         unregAll: function() {
20314
20315             if (this.dragCurrent) {
20316                 this.stopDrag();
20317                 this.dragCurrent = null;
20318             }
20319
20320             this._execOnAll("unreg", []);
20321
20322             for (i in this.elementCache) {
20323                 delete this.elementCache[i];
20324             }
20325
20326             this.elementCache = {};
20327             this.ids = {};
20328         },
20329
20330         /**
20331          * A cache of DOM elements
20332          * @property elementCache
20333          * @private
20334          * @static
20335          */
20336         elementCache: {},
20337
20338         /**
20339          * Get the wrapper for the DOM element specified
20340          * @method getElWrapper
20341          * @param {String} id the id of the element to get
20342          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20343          * @private
20344          * @deprecated This wrapper isn't that useful
20345          * @static
20346          */
20347         getElWrapper: function(id) {
20348             var oWrapper = this.elementCache[id];
20349             if (!oWrapper || !oWrapper.el) {
20350                 oWrapper = this.elementCache[id] =
20351                     new this.ElementWrapper(Roo.getDom(id));
20352             }
20353             return oWrapper;
20354         },
20355
20356         /**
20357          * Returns the actual DOM element
20358          * @method getElement
20359          * @param {String} id the id of the elment to get
20360          * @return {Object} The element
20361          * @deprecated use Roo.getDom instead
20362          * @static
20363          */
20364         getElement: function(id) {
20365             return Roo.getDom(id);
20366         },
20367
20368         /**
20369          * Returns the style property for the DOM element (i.e.,
20370          * document.getElById(id).style)
20371          * @method getCss
20372          * @param {String} id the id of the elment to get
20373          * @return {Object} The style property of the element
20374          * @deprecated use Roo.getDom instead
20375          * @static
20376          */
20377         getCss: function(id) {
20378             var el = Roo.getDom(id);
20379             return (el) ? el.style : null;
20380         },
20381
20382         /**
20383          * Inner class for cached elements
20384          * @class DragDropMgr.ElementWrapper
20385          * @for DragDropMgr
20386          * @private
20387          * @deprecated
20388          */
20389         ElementWrapper: function(el) {
20390                 /**
20391                  * The element
20392                  * @property el
20393                  */
20394                 this.el = el || null;
20395                 /**
20396                  * The element id
20397                  * @property id
20398                  */
20399                 this.id = this.el && el.id;
20400                 /**
20401                  * A reference to the style property
20402                  * @property css
20403                  */
20404                 this.css = this.el && el.style;
20405             },
20406
20407         /**
20408          * Returns the X position of an html element
20409          * @method getPosX
20410          * @param el the element for which to get the position
20411          * @return {int} the X coordinate
20412          * @for DragDropMgr
20413          * @deprecated use Roo.lib.Dom.getX instead
20414          * @static
20415          */
20416         getPosX: function(el) {
20417             return Roo.lib.Dom.getX(el);
20418         },
20419
20420         /**
20421          * Returns the Y position of an html element
20422          * @method getPosY
20423          * @param el the element for which to get the position
20424          * @return {int} the Y coordinate
20425          * @deprecated use Roo.lib.Dom.getY instead
20426          * @static
20427          */
20428         getPosY: function(el) {
20429             return Roo.lib.Dom.getY(el);
20430         },
20431
20432         /**
20433          * Swap two nodes.  In IE, we use the native method, for others we
20434          * emulate the IE behavior
20435          * @method swapNode
20436          * @param n1 the first node to swap
20437          * @param n2 the other node to swap
20438          * @static
20439          */
20440         swapNode: function(n1, n2) {
20441             if (n1.swapNode) {
20442                 n1.swapNode(n2);
20443             } else {
20444                 var p = n2.parentNode;
20445                 var s = n2.nextSibling;
20446
20447                 if (s == n1) {
20448                     p.insertBefore(n1, n2);
20449                 } else if (n2 == n1.nextSibling) {
20450                     p.insertBefore(n2, n1);
20451                 } else {
20452                     n1.parentNode.replaceChild(n2, n1);
20453                     p.insertBefore(n1, s);
20454                 }
20455             }
20456         },
20457
20458         /**
20459          * Returns the current scroll position
20460          * @method getScroll
20461          * @private
20462          * @static
20463          */
20464         getScroll: function () {
20465             var t, l, dde=document.documentElement, db=document.body;
20466             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20467                 t = dde.scrollTop;
20468                 l = dde.scrollLeft;
20469             } else if (db) {
20470                 t = db.scrollTop;
20471                 l = db.scrollLeft;
20472             } else {
20473
20474             }
20475             return { top: t, left: l };
20476         },
20477
20478         /**
20479          * Returns the specified element style property
20480          * @method getStyle
20481          * @param {HTMLElement} el          the element
20482          * @param {string}      styleProp   the style property
20483          * @return {string} The value of the style property
20484          * @deprecated use Roo.lib.Dom.getStyle
20485          * @static
20486          */
20487         getStyle: function(el, styleProp) {
20488             return Roo.fly(el).getStyle(styleProp);
20489         },
20490
20491         /**
20492          * Gets the scrollTop
20493          * @method getScrollTop
20494          * @return {int} the document's scrollTop
20495          * @static
20496          */
20497         getScrollTop: function () { return this.getScroll().top; },
20498
20499         /**
20500          * Gets the scrollLeft
20501          * @method getScrollLeft
20502          * @return {int} the document's scrollTop
20503          * @static
20504          */
20505         getScrollLeft: function () { return this.getScroll().left; },
20506
20507         /**
20508          * Sets the x/y position of an element to the location of the
20509          * target element.
20510          * @method moveToEl
20511          * @param {HTMLElement} moveEl      The element to move
20512          * @param {HTMLElement} targetEl    The position reference element
20513          * @static
20514          */
20515         moveToEl: function (moveEl, targetEl) {
20516             var aCoord = Roo.lib.Dom.getXY(targetEl);
20517             Roo.lib.Dom.setXY(moveEl, aCoord);
20518         },
20519
20520         /**
20521          * Numeric array sort function
20522          * @method numericSort
20523          * @static
20524          */
20525         numericSort: function(a, b) { return (a - b); },
20526
20527         /**
20528          * Internal counter
20529          * @property _timeoutCount
20530          * @private
20531          * @static
20532          */
20533         _timeoutCount: 0,
20534
20535         /**
20536          * Trying to make the load order less important.  Without this we get
20537          * an error if this file is loaded before the Event Utility.
20538          * @method _addListeners
20539          * @private
20540          * @static
20541          */
20542         _addListeners: function() {
20543             var DDM = Roo.dd.DDM;
20544             if ( Roo.lib.Event && document ) {
20545                 DDM._onLoad();
20546             } else {
20547                 if (DDM._timeoutCount > 2000) {
20548                 } else {
20549                     setTimeout(DDM._addListeners, 10);
20550                     if (document && document.body) {
20551                         DDM._timeoutCount += 1;
20552                     }
20553                 }
20554             }
20555         },
20556
20557         /**
20558          * Recursively searches the immediate parent and all child nodes for
20559          * the handle element in order to determine wheter or not it was
20560          * clicked.
20561          * @method handleWasClicked
20562          * @param node the html element to inspect
20563          * @static
20564          */
20565         handleWasClicked: function(node, id) {
20566             if (this.isHandle(id, node.id)) {
20567                 return true;
20568             } else {
20569                 // check to see if this is a text node child of the one we want
20570                 var p = node.parentNode;
20571
20572                 while (p) {
20573                     if (this.isHandle(id, p.id)) {
20574                         return true;
20575                     } else {
20576                         p = p.parentNode;
20577                     }
20578                 }
20579             }
20580
20581             return false;
20582         }
20583
20584     };
20585
20586 }();
20587
20588 // shorter alias, save a few bytes
20589 Roo.dd.DDM = Roo.dd.DragDropMgr;
20590 Roo.dd.DDM._addListeners();
20591
20592 }/*
20593  * Based on:
20594  * Ext JS Library 1.1.1
20595  * Copyright(c) 2006-2007, Ext JS, LLC.
20596  *
20597  * Originally Released Under LGPL - original licence link has changed is not relivant.
20598  *
20599  * Fork - LGPL
20600  * <script type="text/javascript">
20601  */
20602
20603 /**
20604  * @class Roo.dd.DD
20605  * A DragDrop implementation where the linked element follows the
20606  * mouse cursor during a drag.
20607  * @extends Roo.dd.DragDrop
20608  * @constructor
20609  * @param {String} id the id of the linked element
20610  * @param {String} sGroup the group of related DragDrop items
20611  * @param {object} config an object containing configurable attributes
20612  *                Valid properties for DD:
20613  *                    scroll
20614  */
20615 Roo.dd.DD = function(id, sGroup, config) {
20616     if (id) {
20617         this.init(id, sGroup, config);
20618     }
20619 };
20620
20621 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20622
20623     /**
20624      * When set to true, the utility automatically tries to scroll the browser
20625      * window wehn a drag and drop element is dragged near the viewport boundary.
20626      * Defaults to true.
20627      * @property scroll
20628      * @type boolean
20629      */
20630     scroll: true,
20631
20632     /**
20633      * Sets the pointer offset to the distance between the linked element's top
20634      * left corner and the location the element was clicked
20635      * @method autoOffset
20636      * @param {int} iPageX the X coordinate of the click
20637      * @param {int} iPageY the Y coordinate of the click
20638      */
20639     autoOffset: function(iPageX, iPageY) {
20640         var x = iPageX - this.startPageX;
20641         var y = iPageY - this.startPageY;
20642         this.setDelta(x, y);
20643     },
20644
20645     /**
20646      * Sets the pointer offset.  You can call this directly to force the
20647      * offset to be in a particular location (e.g., pass in 0,0 to set it
20648      * to the center of the object)
20649      * @method setDelta
20650      * @param {int} iDeltaX the distance from the left
20651      * @param {int} iDeltaY the distance from the top
20652      */
20653     setDelta: function(iDeltaX, iDeltaY) {
20654         this.deltaX = iDeltaX;
20655         this.deltaY = iDeltaY;
20656     },
20657
20658     /**
20659      * Sets the drag element to the location of the mousedown or click event,
20660      * maintaining the cursor location relative to the location on the element
20661      * that was clicked.  Override this if you want to place the element in a
20662      * location other than where the cursor is.
20663      * @method setDragElPos
20664      * @param {int} iPageX the X coordinate of the mousedown or drag event
20665      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20666      */
20667     setDragElPos: function(iPageX, iPageY) {
20668         // the first time we do this, we are going to check to make sure
20669         // the element has css positioning
20670
20671         var el = this.getDragEl();
20672         this.alignElWithMouse(el, iPageX, iPageY);
20673     },
20674
20675     /**
20676      * Sets the element to the location of the mousedown or click event,
20677      * maintaining the cursor location relative to the location on the element
20678      * that was clicked.  Override this if you want to place the element in a
20679      * location other than where the cursor is.
20680      * @method alignElWithMouse
20681      * @param {HTMLElement} el the element to move
20682      * @param {int} iPageX the X coordinate of the mousedown or drag event
20683      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20684      */
20685     alignElWithMouse: function(el, iPageX, iPageY) {
20686         var oCoord = this.getTargetCoord(iPageX, iPageY);
20687         var fly = el.dom ? el : Roo.fly(el);
20688         if (!this.deltaSetXY) {
20689             var aCoord = [oCoord.x, oCoord.y];
20690             fly.setXY(aCoord);
20691             var newLeft = fly.getLeft(true);
20692             var newTop  = fly.getTop(true);
20693             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20694         } else {
20695             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20696         }
20697
20698         this.cachePosition(oCoord.x, oCoord.y);
20699         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20700         return oCoord;
20701     },
20702
20703     /**
20704      * Saves the most recent position so that we can reset the constraints and
20705      * tick marks on-demand.  We need to know this so that we can calculate the
20706      * number of pixels the element is offset from its original position.
20707      * @method cachePosition
20708      * @param iPageX the current x position (optional, this just makes it so we
20709      * don't have to look it up again)
20710      * @param iPageY the current y position (optional, this just makes it so we
20711      * don't have to look it up again)
20712      */
20713     cachePosition: function(iPageX, iPageY) {
20714         if (iPageX) {
20715             this.lastPageX = iPageX;
20716             this.lastPageY = iPageY;
20717         } else {
20718             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20719             this.lastPageX = aCoord[0];
20720             this.lastPageY = aCoord[1];
20721         }
20722     },
20723
20724     /**
20725      * Auto-scroll the window if the dragged object has been moved beyond the
20726      * visible window boundary.
20727      * @method autoScroll
20728      * @param {int} x the drag element's x position
20729      * @param {int} y the drag element's y position
20730      * @param {int} h the height of the drag element
20731      * @param {int} w the width of the drag element
20732      * @private
20733      */
20734     autoScroll: function(x, y, h, w) {
20735
20736         if (this.scroll) {
20737             // The client height
20738             var clientH = Roo.lib.Dom.getViewWidth();
20739
20740             // The client width
20741             var clientW = Roo.lib.Dom.getViewHeight();
20742
20743             // The amt scrolled down
20744             var st = this.DDM.getScrollTop();
20745
20746             // The amt scrolled right
20747             var sl = this.DDM.getScrollLeft();
20748
20749             // Location of the bottom of the element
20750             var bot = h + y;
20751
20752             // Location of the right of the element
20753             var right = w + x;
20754
20755             // The distance from the cursor to the bottom of the visible area,
20756             // adjusted so that we don't scroll if the cursor is beyond the
20757             // element drag constraints
20758             var toBot = (clientH + st - y - this.deltaY);
20759
20760             // The distance from the cursor to the right of the visible area
20761             var toRight = (clientW + sl - x - this.deltaX);
20762
20763
20764             // How close to the edge the cursor must be before we scroll
20765             // var thresh = (document.all) ? 100 : 40;
20766             var thresh = 40;
20767
20768             // How many pixels to scroll per autoscroll op.  This helps to reduce
20769             // clunky scrolling. IE is more sensitive about this ... it needs this
20770             // value to be higher.
20771             var scrAmt = (document.all) ? 80 : 30;
20772
20773             // Scroll down if we are near the bottom of the visible page and the
20774             // obj extends below the crease
20775             if ( bot > clientH && toBot < thresh ) {
20776                 window.scrollTo(sl, st + scrAmt);
20777             }
20778
20779             // Scroll up if the window is scrolled down and the top of the object
20780             // goes above the top border
20781             if ( y < st && st > 0 && y - st < thresh ) {
20782                 window.scrollTo(sl, st - scrAmt);
20783             }
20784
20785             // Scroll right if the obj is beyond the right border and the cursor is
20786             // near the border.
20787             if ( right > clientW && toRight < thresh ) {
20788                 window.scrollTo(sl + scrAmt, st);
20789             }
20790
20791             // Scroll left if the window has been scrolled to the right and the obj
20792             // extends past the left border
20793             if ( x < sl && sl > 0 && x - sl < thresh ) {
20794                 window.scrollTo(sl - scrAmt, st);
20795             }
20796         }
20797     },
20798
20799     /**
20800      * Finds the location the element should be placed if we want to move
20801      * it to where the mouse location less the click offset would place us.
20802      * @method getTargetCoord
20803      * @param {int} iPageX the X coordinate of the click
20804      * @param {int} iPageY the Y coordinate of the click
20805      * @return an object that contains the coordinates (Object.x and Object.y)
20806      * @private
20807      */
20808     getTargetCoord: function(iPageX, iPageY) {
20809
20810
20811         var x = iPageX - this.deltaX;
20812         var y = iPageY - this.deltaY;
20813
20814         if (this.constrainX) {
20815             if (x < this.minX) { x = this.minX; }
20816             if (x > this.maxX) { x = this.maxX; }
20817         }
20818
20819         if (this.constrainY) {
20820             if (y < this.minY) { y = this.minY; }
20821             if (y > this.maxY) { y = this.maxY; }
20822         }
20823
20824         x = this.getTick(x, this.xTicks);
20825         y = this.getTick(y, this.yTicks);
20826
20827
20828         return {x:x, y:y};
20829     },
20830
20831     /*
20832      * Sets up config options specific to this class. Overrides
20833      * Roo.dd.DragDrop, but all versions of this method through the
20834      * inheritance chain are called
20835      */
20836     applyConfig: function() {
20837         Roo.dd.DD.superclass.applyConfig.call(this);
20838         this.scroll = (this.config.scroll !== false);
20839     },
20840
20841     /*
20842      * Event that fires prior to the onMouseDown event.  Overrides
20843      * Roo.dd.DragDrop.
20844      */
20845     b4MouseDown: function(e) {
20846         // this.resetConstraints();
20847         this.autoOffset(e.getPageX(),
20848                             e.getPageY());
20849     },
20850
20851     /*
20852      * Event that fires prior to the onDrag event.  Overrides
20853      * Roo.dd.DragDrop.
20854      */
20855     b4Drag: function(e) {
20856         this.setDragElPos(e.getPageX(),
20857                             e.getPageY());
20858     },
20859
20860     toString: function() {
20861         return ("DD " + this.id);
20862     }
20863
20864     //////////////////////////////////////////////////////////////////////////
20865     // Debugging ygDragDrop events that can be overridden
20866     //////////////////////////////////////////////////////////////////////////
20867     /*
20868     startDrag: function(x, y) {
20869     },
20870
20871     onDrag: function(e) {
20872     },
20873
20874     onDragEnter: function(e, id) {
20875     },
20876
20877     onDragOver: function(e, id) {
20878     },
20879
20880     onDragOut: function(e, id) {
20881     },
20882
20883     onDragDrop: function(e, id) {
20884     },
20885
20886     endDrag: function(e) {
20887     }
20888
20889     */
20890
20891 });/*
20892  * Based on:
20893  * Ext JS Library 1.1.1
20894  * Copyright(c) 2006-2007, Ext JS, LLC.
20895  *
20896  * Originally Released Under LGPL - original licence link has changed is not relivant.
20897  *
20898  * Fork - LGPL
20899  * <script type="text/javascript">
20900  */
20901
20902 /**
20903  * @class Roo.dd.DDProxy
20904  * A DragDrop implementation that inserts an empty, bordered div into
20905  * the document that follows the cursor during drag operations.  At the time of
20906  * the click, the frame div is resized to the dimensions of the linked html
20907  * element, and moved to the exact location of the linked element.
20908  *
20909  * References to the "frame" element refer to the single proxy element that
20910  * was created to be dragged in place of all DDProxy elements on the
20911  * page.
20912  *
20913  * @extends Roo.dd.DD
20914  * @constructor
20915  * @param {String} id the id of the linked html element
20916  * @param {String} sGroup the group of related DragDrop objects
20917  * @param {object} config an object containing configurable attributes
20918  *                Valid properties for DDProxy in addition to those in DragDrop:
20919  *                   resizeFrame, centerFrame, dragElId
20920  */
20921 Roo.dd.DDProxy = function(id, sGroup, config) {
20922     if (id) {
20923         this.init(id, sGroup, config);
20924         this.initFrame();
20925     }
20926 };
20927
20928 /**
20929  * The default drag frame div id
20930  * @property Roo.dd.DDProxy.dragElId
20931  * @type String
20932  * @static
20933  */
20934 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20935
20936 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20937
20938     /**
20939      * By default we resize the drag frame to be the same size as the element
20940      * we want to drag (this is to get the frame effect).  We can turn it off
20941      * if we want a different behavior.
20942      * @property resizeFrame
20943      * @type boolean
20944      */
20945     resizeFrame: true,
20946
20947     /**
20948      * By default the frame is positioned exactly where the drag element is, so
20949      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20950      * you do not have constraints on the obj is to have the drag frame centered
20951      * around the cursor.  Set centerFrame to true for this effect.
20952      * @property centerFrame
20953      * @type boolean
20954      */
20955     centerFrame: false,
20956
20957     /**
20958      * Creates the proxy element if it does not yet exist
20959      * @method createFrame
20960      */
20961     createFrame: function() {
20962         var self = this;
20963         var body = document.body;
20964
20965         if (!body || !body.firstChild) {
20966             setTimeout( function() { self.createFrame(); }, 50 );
20967             return;
20968         }
20969
20970         var div = this.getDragEl();
20971
20972         if (!div) {
20973             div    = document.createElement("div");
20974             div.id = this.dragElId;
20975             var s  = div.style;
20976
20977             s.position   = "absolute";
20978             s.visibility = "hidden";
20979             s.cursor     = "move";
20980             s.border     = "2px solid #aaa";
20981             s.zIndex     = 999;
20982
20983             // appendChild can blow up IE if invoked prior to the window load event
20984             // while rendering a table.  It is possible there are other scenarios
20985             // that would cause this to happen as well.
20986             body.insertBefore(div, body.firstChild);
20987         }
20988     },
20989
20990     /**
20991      * Initialization for the drag frame element.  Must be called in the
20992      * constructor of all subclasses
20993      * @method initFrame
20994      */
20995     initFrame: function() {
20996         this.createFrame();
20997     },
20998
20999     applyConfig: function() {
21000         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21001
21002         this.resizeFrame = (this.config.resizeFrame !== false);
21003         this.centerFrame = (this.config.centerFrame);
21004         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21005     },
21006
21007     /**
21008      * Resizes the drag frame to the dimensions of the clicked object, positions
21009      * it over the object, and finally displays it
21010      * @method showFrame
21011      * @param {int} iPageX X click position
21012      * @param {int} iPageY Y click position
21013      * @private
21014      */
21015     showFrame: function(iPageX, iPageY) {
21016         var el = this.getEl();
21017         var dragEl = this.getDragEl();
21018         var s = dragEl.style;
21019
21020         this._resizeProxy();
21021
21022         if (this.centerFrame) {
21023             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21024                            Math.round(parseInt(s.height, 10)/2) );
21025         }
21026
21027         this.setDragElPos(iPageX, iPageY);
21028
21029         Roo.fly(dragEl).show();
21030     },
21031
21032     /**
21033      * The proxy is automatically resized to the dimensions of the linked
21034      * element when a drag is initiated, unless resizeFrame is set to false
21035      * @method _resizeProxy
21036      * @private
21037      */
21038     _resizeProxy: function() {
21039         if (this.resizeFrame) {
21040             var el = this.getEl();
21041             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21042         }
21043     },
21044
21045     // overrides Roo.dd.DragDrop
21046     b4MouseDown: function(e) {
21047         var x = e.getPageX();
21048         var y = e.getPageY();
21049         this.autoOffset(x, y);
21050         this.setDragElPos(x, y);
21051     },
21052
21053     // overrides Roo.dd.DragDrop
21054     b4StartDrag: function(x, y) {
21055         // show the drag frame
21056         this.showFrame(x, y);
21057     },
21058
21059     // overrides Roo.dd.DragDrop
21060     b4EndDrag: function(e) {
21061         Roo.fly(this.getDragEl()).hide();
21062     },
21063
21064     // overrides Roo.dd.DragDrop
21065     // By default we try to move the element to the last location of the frame.
21066     // This is so that the default behavior mirrors that of Roo.dd.DD.
21067     endDrag: function(e) {
21068
21069         var lel = this.getEl();
21070         var del = this.getDragEl();
21071
21072         // Show the drag frame briefly so we can get its position
21073         del.style.visibility = "";
21074
21075         this.beforeMove();
21076         // Hide the linked element before the move to get around a Safari
21077         // rendering bug.
21078         lel.style.visibility = "hidden";
21079         Roo.dd.DDM.moveToEl(lel, del);
21080         del.style.visibility = "hidden";
21081         lel.style.visibility = "";
21082
21083         this.afterDrag();
21084     },
21085
21086     beforeMove : function(){
21087
21088     },
21089
21090     afterDrag : function(){
21091
21092     },
21093
21094     toString: function() {
21095         return ("DDProxy " + this.id);
21096     }
21097
21098 });
21099 /*
21100  * Based on:
21101  * Ext JS Library 1.1.1
21102  * Copyright(c) 2006-2007, Ext JS, LLC.
21103  *
21104  * Originally Released Under LGPL - original licence link has changed is not relivant.
21105  *
21106  * Fork - LGPL
21107  * <script type="text/javascript">
21108  */
21109
21110  /**
21111  * @class Roo.dd.DDTarget
21112  * A DragDrop implementation that does not move, but can be a drop
21113  * target.  You would get the same result by simply omitting implementation
21114  * for the event callbacks, but this way we reduce the processing cost of the
21115  * event listener and the callbacks.
21116  * @extends Roo.dd.DragDrop
21117  * @constructor
21118  * @param {String} id the id of the element that is a drop target
21119  * @param {String} sGroup the group of related DragDrop objects
21120  * @param {object} config an object containing configurable attributes
21121  *                 Valid properties for DDTarget in addition to those in
21122  *                 DragDrop:
21123  *                    none
21124  */
21125 Roo.dd.DDTarget = function(id, sGroup, config) {
21126     if (id) {
21127         this.initTarget(id, sGroup, config);
21128     }
21129     if (config && (config.listeners || config.events)) { 
21130         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21131             listeners : config.listeners || {}, 
21132             events : config.events || {} 
21133         });    
21134     }
21135 };
21136
21137 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21138 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21139     toString: function() {
21140         return ("DDTarget " + this.id);
21141     }
21142 });
21143 /*
21144  * Based on:
21145  * Ext JS Library 1.1.1
21146  * Copyright(c) 2006-2007, Ext JS, LLC.
21147  *
21148  * Originally Released Under LGPL - original licence link has changed is not relivant.
21149  *
21150  * Fork - LGPL
21151  * <script type="text/javascript">
21152  */
21153  
21154
21155 /**
21156  * @class Roo.dd.ScrollManager
21157  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21158  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21159  * @singleton
21160  */
21161 Roo.dd.ScrollManager = function(){
21162     var ddm = Roo.dd.DragDropMgr;
21163     var els = {};
21164     var dragEl = null;
21165     var proc = {};
21166     
21167     
21168     
21169     var onStop = function(e){
21170         dragEl = null;
21171         clearProc();
21172     };
21173     
21174     var triggerRefresh = function(){
21175         if(ddm.dragCurrent){
21176              ddm.refreshCache(ddm.dragCurrent.groups);
21177         }
21178     };
21179     
21180     var doScroll = function(){
21181         if(ddm.dragCurrent){
21182             var dds = Roo.dd.ScrollManager;
21183             if(!dds.animate){
21184                 if(proc.el.scroll(proc.dir, dds.increment)){
21185                     triggerRefresh();
21186                 }
21187             }else{
21188                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21189             }
21190         }
21191     };
21192     
21193     var clearProc = function(){
21194         if(proc.id){
21195             clearInterval(proc.id);
21196         }
21197         proc.id = 0;
21198         proc.el = null;
21199         proc.dir = "";
21200     };
21201     
21202     var startProc = function(el, dir){
21203          Roo.log('scroll startproc');
21204         clearProc();
21205         proc.el = el;
21206         proc.dir = dir;
21207         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21208     };
21209     
21210     var onFire = function(e, isDrop){
21211        
21212         if(isDrop || !ddm.dragCurrent){ return; }
21213         var dds = Roo.dd.ScrollManager;
21214         if(!dragEl || dragEl != ddm.dragCurrent){
21215             dragEl = ddm.dragCurrent;
21216             // refresh regions on drag start
21217             dds.refreshCache();
21218         }
21219         
21220         var xy = Roo.lib.Event.getXY(e);
21221         var pt = new Roo.lib.Point(xy[0], xy[1]);
21222         for(var id in els){
21223             var el = els[id], r = el._region;
21224             if(r && r.contains(pt) && el.isScrollable()){
21225                 if(r.bottom - pt.y <= dds.thresh){
21226                     if(proc.el != el){
21227                         startProc(el, "down");
21228                     }
21229                     return;
21230                 }else if(r.right - pt.x <= dds.thresh){
21231                     if(proc.el != el){
21232                         startProc(el, "left");
21233                     }
21234                     return;
21235                 }else if(pt.y - r.top <= dds.thresh){
21236                     if(proc.el != el){
21237                         startProc(el, "up");
21238                     }
21239                     return;
21240                 }else if(pt.x - r.left <= dds.thresh){
21241                     if(proc.el != el){
21242                         startProc(el, "right");
21243                     }
21244                     return;
21245                 }
21246             }
21247         }
21248         clearProc();
21249     };
21250     
21251     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21252     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21253     
21254     return {
21255         /**
21256          * Registers new overflow element(s) to auto scroll
21257          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21258          */
21259         register : function(el){
21260             if(el instanceof Array){
21261                 for(var i = 0, len = el.length; i < len; i++) {
21262                         this.register(el[i]);
21263                 }
21264             }else{
21265                 el = Roo.get(el);
21266                 els[el.id] = el;
21267             }
21268             Roo.dd.ScrollManager.els = els;
21269         },
21270         
21271         /**
21272          * Unregisters overflow element(s) so they are no longer scrolled
21273          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21274          */
21275         unregister : function(el){
21276             if(el instanceof Array){
21277                 for(var i = 0, len = el.length; i < len; i++) {
21278                         this.unregister(el[i]);
21279                 }
21280             }else{
21281                 el = Roo.get(el);
21282                 delete els[el.id];
21283             }
21284         },
21285         
21286         /**
21287          * The number of pixels from the edge of a container the pointer needs to be to 
21288          * trigger scrolling (defaults to 25)
21289          * @type Number
21290          */
21291         thresh : 25,
21292         
21293         /**
21294          * The number of pixels to scroll in each scroll increment (defaults to 50)
21295          * @type Number
21296          */
21297         increment : 100,
21298         
21299         /**
21300          * The frequency of scrolls in milliseconds (defaults to 500)
21301          * @type Number
21302          */
21303         frequency : 500,
21304         
21305         /**
21306          * True to animate the scroll (defaults to true)
21307          * @type Boolean
21308          */
21309         animate: true,
21310         
21311         /**
21312          * The animation duration in seconds - 
21313          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21314          * @type Number
21315          */
21316         animDuration: .4,
21317         
21318         /**
21319          * Manually trigger a cache refresh.
21320          */
21321         refreshCache : function(){
21322             for(var id in els){
21323                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21324                     els[id]._region = els[id].getRegion();
21325                 }
21326             }
21327         }
21328     };
21329 }();/*
21330  * Based on:
21331  * Ext JS Library 1.1.1
21332  * Copyright(c) 2006-2007, Ext JS, LLC.
21333  *
21334  * Originally Released Under LGPL - original licence link has changed is not relivant.
21335  *
21336  * Fork - LGPL
21337  * <script type="text/javascript">
21338  */
21339  
21340
21341 /**
21342  * @class Roo.dd.Registry
21343  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21344  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21345  * @singleton
21346  */
21347 Roo.dd.Registry = function(){
21348     var elements = {}; 
21349     var handles = {}; 
21350     var autoIdSeed = 0;
21351
21352     var getId = function(el, autogen){
21353         if(typeof el == "string"){
21354             return el;
21355         }
21356         var id = el.id;
21357         if(!id && autogen !== false){
21358             id = "roodd-" + (++autoIdSeed);
21359             el.id = id;
21360         }
21361         return id;
21362     };
21363     
21364     return {
21365     /**
21366      * Register a drag drop element
21367      * @param {String|HTMLElement} element The id or DOM node to register
21368      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21369      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21370      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21371      * populated in the data object (if applicable):
21372      * <pre>
21373 Value      Description<br />
21374 ---------  ------------------------------------------<br />
21375 handles    Array of DOM nodes that trigger dragging<br />
21376            for the element being registered<br />
21377 isHandle   True if the element passed in triggers<br />
21378            dragging itself, else false
21379 </pre>
21380      */
21381         register : function(el, data){
21382             data = data || {};
21383             if(typeof el == "string"){
21384                 el = document.getElementById(el);
21385             }
21386             data.ddel = el;
21387             elements[getId(el)] = data;
21388             if(data.isHandle !== false){
21389                 handles[data.ddel.id] = data;
21390             }
21391             if(data.handles){
21392                 var hs = data.handles;
21393                 for(var i = 0, len = hs.length; i < len; i++){
21394                         handles[getId(hs[i])] = data;
21395                 }
21396             }
21397         },
21398
21399     /**
21400      * Unregister a drag drop element
21401      * @param {String|HTMLElement}  element The id or DOM node to unregister
21402      */
21403         unregister : function(el){
21404             var id = getId(el, false);
21405             var data = elements[id];
21406             if(data){
21407                 delete elements[id];
21408                 if(data.handles){
21409                     var hs = data.handles;
21410                     for(var i = 0, len = hs.length; i < len; i++){
21411                         delete handles[getId(hs[i], false)];
21412                     }
21413                 }
21414             }
21415         },
21416
21417     /**
21418      * Returns the handle registered for a DOM Node by id
21419      * @param {String|HTMLElement} id The DOM node or id to look up
21420      * @return {Object} handle The custom handle data
21421      */
21422         getHandle : function(id){
21423             if(typeof id != "string"){ // must be element?
21424                 id = id.id;
21425             }
21426             return handles[id];
21427         },
21428
21429     /**
21430      * Returns the handle that is registered for the DOM node that is the target of the event
21431      * @param {Event} e The event
21432      * @return {Object} handle The custom handle data
21433      */
21434         getHandleFromEvent : function(e){
21435             var t = Roo.lib.Event.getTarget(e);
21436             return t ? handles[t.id] : null;
21437         },
21438
21439     /**
21440      * Returns a custom data object that is registered for a DOM node by id
21441      * @param {String|HTMLElement} id The DOM node or id to look up
21442      * @return {Object} data The custom data
21443      */
21444         getTarget : function(id){
21445             if(typeof id != "string"){ // must be element?
21446                 id = id.id;
21447             }
21448             return elements[id];
21449         },
21450
21451     /**
21452      * Returns a custom data object that is registered for the DOM node that is the target of the event
21453      * @param {Event} e The event
21454      * @return {Object} data The custom data
21455      */
21456         getTargetFromEvent : function(e){
21457             var t = Roo.lib.Event.getTarget(e);
21458             return t ? elements[t.id] || handles[t.id] : null;
21459         }
21460     };
21461 }();/*
21462  * Based on:
21463  * Ext JS Library 1.1.1
21464  * Copyright(c) 2006-2007, Ext JS, LLC.
21465  *
21466  * Originally Released Under LGPL - original licence link has changed is not relivant.
21467  *
21468  * Fork - LGPL
21469  * <script type="text/javascript">
21470  */
21471  
21472
21473 /**
21474  * @class Roo.dd.StatusProxy
21475  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21476  * default drag proxy used by all Roo.dd components.
21477  * @constructor
21478  * @param {Object} config
21479  */
21480 Roo.dd.StatusProxy = function(config){
21481     Roo.apply(this, config);
21482     this.id = this.id || Roo.id();
21483     this.el = new Roo.Layer({
21484         dh: {
21485             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21486                 {tag: "div", cls: "x-dd-drop-icon"},
21487                 {tag: "div", cls: "x-dd-drag-ghost"}
21488             ]
21489         }, 
21490         shadow: !config || config.shadow !== false
21491     });
21492     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21493     this.dropStatus = this.dropNotAllowed;
21494 };
21495
21496 Roo.dd.StatusProxy.prototype = {
21497     /**
21498      * @cfg {String} dropAllowed
21499      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21500      */
21501     dropAllowed : "x-dd-drop-ok",
21502     /**
21503      * @cfg {String} dropNotAllowed
21504      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21505      */
21506     dropNotAllowed : "x-dd-drop-nodrop",
21507
21508     /**
21509      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21510      * over the current target element.
21511      * @param {String} cssClass The css class for the new drop status indicator image
21512      */
21513     setStatus : function(cssClass){
21514         cssClass = cssClass || this.dropNotAllowed;
21515         if(this.dropStatus != cssClass){
21516             this.el.replaceClass(this.dropStatus, cssClass);
21517             this.dropStatus = cssClass;
21518         }
21519     },
21520
21521     /**
21522      * Resets the status indicator to the default dropNotAllowed value
21523      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21524      */
21525     reset : function(clearGhost){
21526         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21527         this.dropStatus = this.dropNotAllowed;
21528         if(clearGhost){
21529             this.ghost.update("");
21530         }
21531     },
21532
21533     /**
21534      * Updates the contents of the ghost element
21535      * @param {String} html The html that will replace the current innerHTML of the ghost element
21536      */
21537     update : function(html){
21538         if(typeof html == "string"){
21539             this.ghost.update(html);
21540         }else{
21541             this.ghost.update("");
21542             html.style.margin = "0";
21543             this.ghost.dom.appendChild(html);
21544         }
21545         // ensure float = none set?? cant remember why though.
21546         var el = this.ghost.dom.firstChild;
21547                 if(el){
21548                         Roo.fly(el).setStyle('float', 'none');
21549                 }
21550     },
21551     
21552     /**
21553      * Returns the underlying proxy {@link Roo.Layer}
21554      * @return {Roo.Layer} el
21555     */
21556     getEl : function(){
21557         return this.el;
21558     },
21559
21560     /**
21561      * Returns the ghost element
21562      * @return {Roo.Element} el
21563      */
21564     getGhost : function(){
21565         return this.ghost;
21566     },
21567
21568     /**
21569      * Hides the proxy
21570      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21571      */
21572     hide : function(clear){
21573         this.el.hide();
21574         if(clear){
21575             this.reset(true);
21576         }
21577     },
21578
21579     /**
21580      * Stops the repair animation if it's currently running
21581      */
21582     stop : function(){
21583         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21584             this.anim.stop();
21585         }
21586     },
21587
21588     /**
21589      * Displays this proxy
21590      */
21591     show : function(){
21592         this.el.show();
21593     },
21594
21595     /**
21596      * Force the Layer to sync its shadow and shim positions to the element
21597      */
21598     sync : function(){
21599         this.el.sync();
21600     },
21601
21602     /**
21603      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21604      * invalid drop operation by the item being dragged.
21605      * @param {Array} xy The XY position of the element ([x, y])
21606      * @param {Function} callback The function to call after the repair is complete
21607      * @param {Object} scope The scope in which to execute the callback
21608      */
21609     repair : function(xy, callback, scope){
21610         this.callback = callback;
21611         this.scope = scope;
21612         if(xy && this.animRepair !== false){
21613             this.el.addClass("x-dd-drag-repair");
21614             this.el.hideUnders(true);
21615             this.anim = this.el.shift({
21616                 duration: this.repairDuration || .5,
21617                 easing: 'easeOut',
21618                 xy: xy,
21619                 stopFx: true,
21620                 callback: this.afterRepair,
21621                 scope: this
21622             });
21623         }else{
21624             this.afterRepair();
21625         }
21626     },
21627
21628     // private
21629     afterRepair : function(){
21630         this.hide(true);
21631         if(typeof this.callback == "function"){
21632             this.callback.call(this.scope || this);
21633         }
21634         this.callback = null;
21635         this.scope = null;
21636     }
21637 };/*
21638  * Based on:
21639  * Ext JS Library 1.1.1
21640  * Copyright(c) 2006-2007, Ext JS, LLC.
21641  *
21642  * Originally Released Under LGPL - original licence link has changed is not relivant.
21643  *
21644  * Fork - LGPL
21645  * <script type="text/javascript">
21646  */
21647
21648 /**
21649  * @class Roo.dd.DragSource
21650  * @extends Roo.dd.DDProxy
21651  * A simple class that provides the basic implementation needed to make any element draggable.
21652  * @constructor
21653  * @param {String/HTMLElement/Element} el The container element
21654  * @param {Object} config
21655  */
21656 Roo.dd.DragSource = function(el, config){
21657     this.el = Roo.get(el);
21658     this.dragData = {};
21659     
21660     Roo.apply(this, config);
21661     
21662     if(!this.proxy){
21663         this.proxy = new Roo.dd.StatusProxy();
21664     }
21665
21666     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21667           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21668     
21669     this.dragging = false;
21670 };
21671
21672 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21673     /**
21674      * @cfg {String} dropAllowed
21675      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21676      */
21677     dropAllowed : "x-dd-drop-ok",
21678     /**
21679      * @cfg {String} dropNotAllowed
21680      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21681      */
21682     dropNotAllowed : "x-dd-drop-nodrop",
21683
21684     /**
21685      * Returns the data object associated with this drag source
21686      * @return {Object} data An object containing arbitrary data
21687      */
21688     getDragData : function(e){
21689         return this.dragData;
21690     },
21691
21692     // private
21693     onDragEnter : function(e, id){
21694         var target = Roo.dd.DragDropMgr.getDDById(id);
21695         this.cachedTarget = target;
21696         if(this.beforeDragEnter(target, e, id) !== false){
21697             if(target.isNotifyTarget){
21698                 var status = target.notifyEnter(this, e, this.dragData);
21699                 this.proxy.setStatus(status);
21700             }else{
21701                 this.proxy.setStatus(this.dropAllowed);
21702             }
21703             
21704             if(this.afterDragEnter){
21705                 /**
21706                  * An empty function by default, but provided so that you can perform a custom action
21707                  * when the dragged item enters the drop target by providing an implementation.
21708                  * @param {Roo.dd.DragDrop} target The drop target
21709                  * @param {Event} e The event object
21710                  * @param {String} id The id of the dragged element
21711                  * @method afterDragEnter
21712                  */
21713                 this.afterDragEnter(target, e, id);
21714             }
21715         }
21716     },
21717
21718     /**
21719      * An empty function by default, but provided so that you can perform a custom action
21720      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21721      * @param {Roo.dd.DragDrop} target The drop target
21722      * @param {Event} e The event object
21723      * @param {String} id The id of the dragged element
21724      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21725      */
21726     beforeDragEnter : function(target, e, id){
21727         return true;
21728     },
21729
21730     // private
21731     alignElWithMouse: function() {
21732         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21733         this.proxy.sync();
21734     },
21735
21736     // private
21737     onDragOver : function(e, id){
21738         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21739         if(this.beforeDragOver(target, e, id) !== false){
21740             if(target.isNotifyTarget){
21741                 var status = target.notifyOver(this, e, this.dragData);
21742                 this.proxy.setStatus(status);
21743             }
21744
21745             if(this.afterDragOver){
21746                 /**
21747                  * An empty function by default, but provided so that you can perform a custom action
21748                  * while the dragged item is over the drop target by providing an implementation.
21749                  * @param {Roo.dd.DragDrop} target The drop target
21750                  * @param {Event} e The event object
21751                  * @param {String} id The id of the dragged element
21752                  * @method afterDragOver
21753                  */
21754                 this.afterDragOver(target, e, id);
21755             }
21756         }
21757     },
21758
21759     /**
21760      * An empty function by default, but provided so that you can perform a custom action
21761      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21762      * @param {Roo.dd.DragDrop} target The drop target
21763      * @param {Event} e The event object
21764      * @param {String} id The id of the dragged element
21765      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21766      */
21767     beforeDragOver : function(target, e, id){
21768         return true;
21769     },
21770
21771     // private
21772     onDragOut : function(e, id){
21773         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21774         if(this.beforeDragOut(target, e, id) !== false){
21775             if(target.isNotifyTarget){
21776                 target.notifyOut(this, e, this.dragData);
21777             }
21778             this.proxy.reset();
21779             if(this.afterDragOut){
21780                 /**
21781                  * An empty function by default, but provided so that you can perform a custom action
21782                  * after the dragged item is dragged out of the target without dropping.
21783                  * @param {Roo.dd.DragDrop} target The drop target
21784                  * @param {Event} e The event object
21785                  * @param {String} id The id of the dragged element
21786                  * @method afterDragOut
21787                  */
21788                 this.afterDragOut(target, e, id);
21789             }
21790         }
21791         this.cachedTarget = null;
21792     },
21793
21794     /**
21795      * An empty function by default, but provided so that you can perform a custom action before the dragged
21796      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21797      * @param {Roo.dd.DragDrop} target The drop target
21798      * @param {Event} e The event object
21799      * @param {String} id The id of the dragged element
21800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21801      */
21802     beforeDragOut : function(target, e, id){
21803         return true;
21804     },
21805     
21806     // private
21807     onDragDrop : function(e, id){
21808         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21809         if(this.beforeDragDrop(target, e, id) !== false){
21810             if(target.isNotifyTarget){
21811                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21812                     this.onValidDrop(target, e, id);
21813                 }else{
21814                     this.onInvalidDrop(target, e, id);
21815                 }
21816             }else{
21817                 this.onValidDrop(target, e, id);
21818             }
21819             
21820             if(this.afterDragDrop){
21821                 /**
21822                  * An empty function by default, but provided so that you can perform a custom action
21823                  * after a valid drag drop has occurred by providing an implementation.
21824                  * @param {Roo.dd.DragDrop} target The drop target
21825                  * @param {Event} e The event object
21826                  * @param {String} id The id of the dropped element
21827                  * @method afterDragDrop
21828                  */
21829                 this.afterDragDrop(target, e, id);
21830             }
21831         }
21832         delete this.cachedTarget;
21833     },
21834
21835     /**
21836      * An empty function by default, but provided so that you can perform a custom action before the dragged
21837      * item is dropped onto the target and optionally cancel the onDragDrop.
21838      * @param {Roo.dd.DragDrop} target The drop target
21839      * @param {Event} e The event object
21840      * @param {String} id The id of the dragged element
21841      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21842      */
21843     beforeDragDrop : function(target, e, id){
21844         return true;
21845     },
21846
21847     // private
21848     onValidDrop : function(target, e, id){
21849         this.hideProxy();
21850         if(this.afterValidDrop){
21851             /**
21852              * An empty function by default, but provided so that you can perform a custom action
21853              * after a valid drop has occurred by providing an implementation.
21854              * @param {Object} target The target DD 
21855              * @param {Event} e The event object
21856              * @param {String} id The id of the dropped element
21857              * @method afterInvalidDrop
21858              */
21859             this.afterValidDrop(target, e, id);
21860         }
21861     },
21862
21863     // private
21864     getRepairXY : function(e, data){
21865         return this.el.getXY();  
21866     },
21867
21868     // private
21869     onInvalidDrop : function(target, e, id){
21870         this.beforeInvalidDrop(target, e, id);
21871         if(this.cachedTarget){
21872             if(this.cachedTarget.isNotifyTarget){
21873                 this.cachedTarget.notifyOut(this, e, this.dragData);
21874             }
21875             this.cacheTarget = null;
21876         }
21877         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21878
21879         if(this.afterInvalidDrop){
21880             /**
21881              * An empty function by default, but provided so that you can perform a custom action
21882              * after an invalid drop has occurred by providing an implementation.
21883              * @param {Event} e The event object
21884              * @param {String} id The id of the dropped element
21885              * @method afterInvalidDrop
21886              */
21887             this.afterInvalidDrop(e, id);
21888         }
21889     },
21890
21891     // private
21892     afterRepair : function(){
21893         if(Roo.enableFx){
21894             this.el.highlight(this.hlColor || "c3daf9");
21895         }
21896         this.dragging = false;
21897     },
21898
21899     /**
21900      * An empty function by default, but provided so that you can perform a custom action after an invalid
21901      * drop has occurred.
21902      * @param {Roo.dd.DragDrop} target The drop target
21903      * @param {Event} e The event object
21904      * @param {String} id The id of the dragged element
21905      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21906      */
21907     beforeInvalidDrop : function(target, e, id){
21908         return true;
21909     },
21910
21911     // private
21912     handleMouseDown : function(e){
21913         if(this.dragging) {
21914             return;
21915         }
21916         var data = this.getDragData(e);
21917         if(data && this.onBeforeDrag(data, e) !== false){
21918             this.dragData = data;
21919             this.proxy.stop();
21920             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21921         } 
21922     },
21923
21924     /**
21925      * An empty function by default, but provided so that you can perform a custom action before the initial
21926      * drag event begins and optionally cancel it.
21927      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21928      * @param {Event} e The event object
21929      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21930      */
21931     onBeforeDrag : function(data, e){
21932         return true;
21933     },
21934
21935     /**
21936      * An empty function by default, but provided so that you can perform a custom action once the initial
21937      * drag event has begun.  The drag cannot be canceled from this function.
21938      * @param {Number} x The x position of the click on the dragged object
21939      * @param {Number} y The y position of the click on the dragged object
21940      */
21941     onStartDrag : Roo.emptyFn,
21942
21943     // private - YUI override
21944     startDrag : function(x, y){
21945         this.proxy.reset();
21946         this.dragging = true;
21947         this.proxy.update("");
21948         this.onInitDrag(x, y);
21949         this.proxy.show();
21950     },
21951
21952     // private
21953     onInitDrag : function(x, y){
21954         var clone = this.el.dom.cloneNode(true);
21955         clone.id = Roo.id(); // prevent duplicate ids
21956         this.proxy.update(clone);
21957         this.onStartDrag(x, y);
21958         return true;
21959     },
21960
21961     /**
21962      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21963      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21964      */
21965     getProxy : function(){
21966         return this.proxy;  
21967     },
21968
21969     /**
21970      * Hides the drag source's {@link Roo.dd.StatusProxy}
21971      */
21972     hideProxy : function(){
21973         this.proxy.hide();  
21974         this.proxy.reset(true);
21975         this.dragging = false;
21976     },
21977
21978     // private
21979     triggerCacheRefresh : function(){
21980         Roo.dd.DDM.refreshCache(this.groups);
21981     },
21982
21983     // private - override to prevent hiding
21984     b4EndDrag: function(e) {
21985     },
21986
21987     // private - override to prevent moving
21988     endDrag : function(e){
21989         this.onEndDrag(this.dragData, e);
21990     },
21991
21992     // private
21993     onEndDrag : function(data, e){
21994     },
21995     
21996     // private - pin to cursor
21997     autoOffset : function(x, y) {
21998         this.setDelta(-12, -20);
21999     }    
22000 });/*
22001  * Based on:
22002  * Ext JS Library 1.1.1
22003  * Copyright(c) 2006-2007, Ext JS, LLC.
22004  *
22005  * Originally Released Under LGPL - original licence link has changed is not relivant.
22006  *
22007  * Fork - LGPL
22008  * <script type="text/javascript">
22009  */
22010
22011
22012 /**
22013  * @class Roo.dd.DropTarget
22014  * @extends Roo.dd.DDTarget
22015  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22016  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22017  * @constructor
22018  * @param {String/HTMLElement/Element} el The container element
22019  * @param {Object} config
22020  */
22021 Roo.dd.DropTarget = function(el, config){
22022     this.el = Roo.get(el);
22023     
22024     var listeners = false; ;
22025     if (config && config.listeners) {
22026         listeners= config.listeners;
22027         delete config.listeners;
22028     }
22029     Roo.apply(this, config);
22030     
22031     if(this.containerScroll){
22032         Roo.dd.ScrollManager.register(this.el);
22033     }
22034     this.addEvents( {
22035          /**
22036          * @scope Roo.dd.DropTarget
22037          */
22038          
22039          /**
22040          * @event enter
22041          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22042          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22043          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22044          * 
22045          * IMPORTANT : it should set this.overClass and this.dropAllowed
22046          * 
22047          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22048          * @param {Event} e The event
22049          * @param {Object} data An object containing arbitrary data supplied by the drag source
22050          */
22051         "enter" : true,
22052         
22053          /**
22054          * @event over
22055          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22056          * This method will be called on every mouse movement while the drag source is over the drop target.
22057          * This default implementation simply returns the dropAllowed config value.
22058          * 
22059          * IMPORTANT : it should set this.dropAllowed
22060          * 
22061          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22062          * @param {Event} e The event
22063          * @param {Object} data An object containing arbitrary data supplied by the drag source
22064          
22065          */
22066         "over" : true,
22067         /**
22068          * @event out
22069          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22070          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22071          * overClass (if any) from the drop element.
22072          * 
22073          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22074          * @param {Event} e The event
22075          * @param {Object} data An object containing arbitrary data supplied by the drag source
22076          */
22077          "out" : true,
22078          
22079         /**
22080          * @event drop
22081          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22082          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22083          * implementation that does something to process the drop event and returns true so that the drag source's
22084          * repair action does not run.
22085          * 
22086          * IMPORTANT : it should set this.success
22087          * 
22088          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22089          * @param {Event} e The event
22090          * @param {Object} data An object containing arbitrary data supplied by the drag source
22091         */
22092          "drop" : true
22093     });
22094             
22095      
22096     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22097         this.el.dom, 
22098         this.ddGroup || this.group,
22099         {
22100             isTarget: true,
22101             listeners : listeners || {} 
22102            
22103         
22104         }
22105     );
22106
22107 };
22108
22109 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22110     /**
22111      * @cfg {String} overClass
22112      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22113      */
22114      /**
22115      * @cfg {String} ddGroup
22116      * The drag drop group to handle drop events for
22117      */
22118      
22119     /**
22120      * @cfg {String} dropAllowed
22121      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22122      */
22123     dropAllowed : "x-dd-drop-ok",
22124     /**
22125      * @cfg {String} dropNotAllowed
22126      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22127      */
22128     dropNotAllowed : "x-dd-drop-nodrop",
22129     /**
22130      * @cfg {boolean} success
22131      * set this after drop listener.. 
22132      */
22133     success : false,
22134     /**
22135      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22136      * if the drop point is valid for over/enter..
22137      */
22138     valid : false,
22139     // private
22140     isTarget : true,
22141
22142     // private
22143     isNotifyTarget : true,
22144     
22145     /**
22146      * @hide
22147      */
22148     notifyEnter : function(dd, e, data)
22149     {
22150         this.valid = true;
22151         this.fireEvent('enter', dd, e, data);
22152         if(this.overClass){
22153             this.el.addClass(this.overClass);
22154         }
22155         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22156             this.valid ? this.dropAllowed : this.dropNotAllowed
22157         );
22158     },
22159
22160     /**
22161      * @hide
22162      */
22163     notifyOver : function(dd, e, data)
22164     {
22165         this.valid = true;
22166         this.fireEvent('over', dd, e, data);
22167         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22168             this.valid ? this.dropAllowed : this.dropNotAllowed
22169         );
22170     },
22171
22172     /**
22173      * @hide
22174      */
22175     notifyOut : function(dd, e, data)
22176     {
22177         this.fireEvent('out', dd, e, data);
22178         if(this.overClass){
22179             this.el.removeClass(this.overClass);
22180         }
22181     },
22182
22183     /**
22184      * @hide
22185      */
22186     notifyDrop : function(dd, e, data)
22187     {
22188         this.success = false;
22189         this.fireEvent('drop', dd, e, data);
22190         return this.success;
22191     }
22192 });/*
22193  * Based on:
22194  * Ext JS Library 1.1.1
22195  * Copyright(c) 2006-2007, Ext JS, LLC.
22196  *
22197  * Originally Released Under LGPL - original licence link has changed is not relivant.
22198  *
22199  * Fork - LGPL
22200  * <script type="text/javascript">
22201  */
22202
22203
22204 /**
22205  * @class Roo.dd.DragZone
22206  * @extends Roo.dd.DragSource
22207  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22208  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22209  * @constructor
22210  * @param {String/HTMLElement/Element} el The container element
22211  * @param {Object} config
22212  */
22213 Roo.dd.DragZone = function(el, config){
22214     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22215     if(this.containerScroll){
22216         Roo.dd.ScrollManager.register(this.el);
22217     }
22218 };
22219
22220 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22221     /**
22222      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22223      * for auto scrolling during drag operations.
22224      */
22225     /**
22226      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22227      * method after a failed drop (defaults to "c3daf9" - light blue)
22228      */
22229
22230     /**
22231      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22232      * for a valid target to drag based on the mouse down. Override this method
22233      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22234      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22235      * @param {EventObject} e The mouse down event
22236      * @return {Object} The dragData
22237      */
22238     getDragData : function(e){
22239         return Roo.dd.Registry.getHandleFromEvent(e);
22240     },
22241     
22242     /**
22243      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22244      * this.dragData.ddel
22245      * @param {Number} x The x position of the click on the dragged object
22246      * @param {Number} y The y position of the click on the dragged object
22247      * @return {Boolean} true to continue the drag, false to cancel
22248      */
22249     onInitDrag : function(x, y){
22250         this.proxy.update(this.dragData.ddel.cloneNode(true));
22251         this.onStartDrag(x, y);
22252         return true;
22253     },
22254     
22255     /**
22256      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22257      */
22258     afterRepair : function(){
22259         if(Roo.enableFx){
22260             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22261         }
22262         this.dragging = false;
22263     },
22264
22265     /**
22266      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22267      * the XY of this.dragData.ddel
22268      * @param {EventObject} e The mouse up event
22269      * @return {Array} The xy location (e.g. [100, 200])
22270      */
22271     getRepairXY : function(e){
22272         return Roo.Element.fly(this.dragData.ddel).getXY();  
22273     }
22274 });/*
22275  * Based on:
22276  * Ext JS Library 1.1.1
22277  * Copyright(c) 2006-2007, Ext JS, LLC.
22278  *
22279  * Originally Released Under LGPL - original licence link has changed is not relivant.
22280  *
22281  * Fork - LGPL
22282  * <script type="text/javascript">
22283  */
22284 /**
22285  * @class Roo.dd.DropZone
22286  * @extends Roo.dd.DropTarget
22287  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22288  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22289  * @constructor
22290  * @param {String/HTMLElement/Element} el The container element
22291  * @param {Object} config
22292  */
22293 Roo.dd.DropZone = function(el, config){
22294     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22295 };
22296
22297 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22298     /**
22299      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22300      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22301      * provide your own custom lookup.
22302      * @param {Event} e The event
22303      * @return {Object} data The custom data
22304      */
22305     getTargetFromEvent : function(e){
22306         return Roo.dd.Registry.getTargetFromEvent(e);
22307     },
22308
22309     /**
22310      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22311      * that it has registered.  This method has no default implementation and should be overridden to provide
22312      * node-specific processing if necessary.
22313      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22314      * {@link #getTargetFromEvent} for this node)
22315      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22316      * @param {Event} e The event
22317      * @param {Object} data An object containing arbitrary data supplied by the drag source
22318      */
22319     onNodeEnter : function(n, dd, e, data){
22320         
22321     },
22322
22323     /**
22324      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22325      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22326      * overridden to provide the proper feedback.
22327      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22328      * {@link #getTargetFromEvent} for this node)
22329      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22330      * @param {Event} e The event
22331      * @param {Object} data An object containing arbitrary data supplied by the drag source
22332      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22333      * underlying {@link Roo.dd.StatusProxy} can be updated
22334      */
22335     onNodeOver : function(n, dd, e, data){
22336         return this.dropAllowed;
22337     },
22338
22339     /**
22340      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22341      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22342      * node-specific processing if necessary.
22343      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22344      * {@link #getTargetFromEvent} for this node)
22345      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22346      * @param {Event} e The event
22347      * @param {Object} data An object containing arbitrary data supplied by the drag source
22348      */
22349     onNodeOut : function(n, dd, e, data){
22350         
22351     },
22352
22353     /**
22354      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22355      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22356      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22357      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22358      * {@link #getTargetFromEvent} for this node)
22359      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22360      * @param {Event} e The event
22361      * @param {Object} data An object containing arbitrary data supplied by the drag source
22362      * @return {Boolean} True if the drop was valid, else false
22363      */
22364     onNodeDrop : function(n, dd, e, data){
22365         return false;
22366     },
22367
22368     /**
22369      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22370      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22371      * it should be overridden to provide the proper feedback if necessary.
22372      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22373      * @param {Event} e The event
22374      * @param {Object} data An object containing arbitrary data supplied by the drag source
22375      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22376      * underlying {@link Roo.dd.StatusProxy} can be updated
22377      */
22378     onContainerOver : function(dd, e, data){
22379         return this.dropNotAllowed;
22380     },
22381
22382     /**
22383      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22384      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22385      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22386      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22387      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22388      * @param {Event} e The event
22389      * @param {Object} data An object containing arbitrary data supplied by the drag source
22390      * @return {Boolean} True if the drop was valid, else false
22391      */
22392     onContainerDrop : function(dd, e, data){
22393         return false;
22394     },
22395
22396     /**
22397      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22398      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22399      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22400      * you should override this method and provide a custom implementation.
22401      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22402      * @param {Event} e The event
22403      * @param {Object} data An object containing arbitrary data supplied by the drag source
22404      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22405      * underlying {@link Roo.dd.StatusProxy} can be updated
22406      */
22407     notifyEnter : function(dd, e, data){
22408         return this.dropNotAllowed;
22409     },
22410
22411     /**
22412      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22413      * This method will be called on every mouse movement while the drag source is over the drop zone.
22414      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22415      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22416      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22417      * registered node, it will call {@link #onContainerOver}.
22418      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22419      * @param {Event} e The event
22420      * @param {Object} data An object containing arbitrary data supplied by the drag source
22421      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22422      * underlying {@link Roo.dd.StatusProxy} can be updated
22423      */
22424     notifyOver : function(dd, e, data){
22425         var n = this.getTargetFromEvent(e);
22426         if(!n){ // not over valid drop target
22427             if(this.lastOverNode){
22428                 this.onNodeOut(this.lastOverNode, dd, e, data);
22429                 this.lastOverNode = null;
22430             }
22431             return this.onContainerOver(dd, e, data);
22432         }
22433         if(this.lastOverNode != n){
22434             if(this.lastOverNode){
22435                 this.onNodeOut(this.lastOverNode, dd, e, data);
22436             }
22437             this.onNodeEnter(n, dd, e, data);
22438             this.lastOverNode = n;
22439         }
22440         return this.onNodeOver(n, dd, e, data);
22441     },
22442
22443     /**
22444      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22445      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22446      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22447      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22448      * @param {Event} e The event
22449      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22450      */
22451     notifyOut : function(dd, e, data){
22452         if(this.lastOverNode){
22453             this.onNodeOut(this.lastOverNode, dd, e, data);
22454             this.lastOverNode = null;
22455         }
22456     },
22457
22458     /**
22459      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22460      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22461      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22462      * otherwise it will call {@link #onContainerDrop}.
22463      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22464      * @param {Event} e The event
22465      * @param {Object} data An object containing arbitrary data supplied by the drag source
22466      * @return {Boolean} True if the drop was valid, else false
22467      */
22468     notifyDrop : function(dd, e, data){
22469         if(this.lastOverNode){
22470             this.onNodeOut(this.lastOverNode, dd, e, data);
22471             this.lastOverNode = null;
22472         }
22473         var n = this.getTargetFromEvent(e);
22474         return n ?
22475             this.onNodeDrop(n, dd, e, data) :
22476             this.onContainerDrop(dd, e, data);
22477     },
22478
22479     // private
22480     triggerCacheRefresh : function(){
22481         Roo.dd.DDM.refreshCache(this.groups);
22482     }  
22483 });/*
22484  * Based on:
22485  * Ext JS Library 1.1.1
22486  * Copyright(c) 2006-2007, Ext JS, LLC.
22487  *
22488  * Originally Released Under LGPL - original licence link has changed is not relivant.
22489  *
22490  * Fork - LGPL
22491  * <script type="text/javascript">
22492  */
22493
22494
22495 /**
22496  * @class Roo.data.SortTypes
22497  * @singleton
22498  * Defines the default sorting (casting?) comparison functions used when sorting data.
22499  */
22500 Roo.data.SortTypes = {
22501     /**
22502      * Default sort that does nothing
22503      * @param {Mixed} s The value being converted
22504      * @return {Mixed} The comparison value
22505      */
22506     none : function(s){
22507         return s;
22508     },
22509     
22510     /**
22511      * The regular expression used to strip tags
22512      * @type {RegExp}
22513      * @property
22514      */
22515     stripTagsRE : /<\/?[^>]+>/gi,
22516     
22517     /**
22518      * Strips all HTML tags to sort on text only
22519      * @param {Mixed} s The value being converted
22520      * @return {String} The comparison value
22521      */
22522     asText : function(s){
22523         return String(s).replace(this.stripTagsRE, "");
22524     },
22525     
22526     /**
22527      * Strips all HTML tags to sort on text only - Case insensitive
22528      * @param {Mixed} s The value being converted
22529      * @return {String} The comparison value
22530      */
22531     asUCText : function(s){
22532         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22533     },
22534     
22535     /**
22536      * Case insensitive string
22537      * @param {Mixed} s The value being converted
22538      * @return {String} The comparison value
22539      */
22540     asUCString : function(s) {
22541         return String(s).toUpperCase();
22542     },
22543     
22544     /**
22545      * Date sorting
22546      * @param {Mixed} s The value being converted
22547      * @return {Number} The comparison value
22548      */
22549     asDate : function(s) {
22550         if(!s){
22551             return 0;
22552         }
22553         if(s instanceof Date){
22554             return s.getTime();
22555         }
22556         return Date.parse(String(s));
22557     },
22558     
22559     /**
22560      * Float sorting
22561      * @param {Mixed} s The value being converted
22562      * @return {Float} The comparison value
22563      */
22564     asFloat : function(s) {
22565         var val = parseFloat(String(s).replace(/,/g, ""));
22566         if(isNaN(val)) {
22567             val = 0;
22568         }
22569         return val;
22570     },
22571     
22572     /**
22573      * Integer sorting
22574      * @param {Mixed} s The value being converted
22575      * @return {Number} The comparison value
22576      */
22577     asInt : function(s) {
22578         var val = parseInt(String(s).replace(/,/g, ""));
22579         if(isNaN(val)) {
22580             val = 0;
22581         }
22582         return val;
22583     }
22584 };/*
22585  * Based on:
22586  * Ext JS Library 1.1.1
22587  * Copyright(c) 2006-2007, Ext JS, LLC.
22588  *
22589  * Originally Released Under LGPL - original licence link has changed is not relivant.
22590  *
22591  * Fork - LGPL
22592  * <script type="text/javascript">
22593  */
22594
22595 /**
22596 * @class Roo.data.Record
22597  * Instances of this class encapsulate both record <em>definition</em> information, and record
22598  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22599  * to access Records cached in an {@link Roo.data.Store} object.<br>
22600  * <p>
22601  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22602  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22603  * objects.<br>
22604  * <p>
22605  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22606  * @constructor
22607  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22608  * {@link #create}. The parameters are the same.
22609  * @param {Array} data An associative Array of data values keyed by the field name.
22610  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22611  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22612  * not specified an integer id is generated.
22613  */
22614 Roo.data.Record = function(data, id){
22615     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22616     this.data = data;
22617 };
22618
22619 /**
22620  * Generate a constructor for a specific record layout.
22621  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22622  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22623  * Each field definition object may contain the following properties: <ul>
22624  * <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,
22625  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22626  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22627  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22628  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22629  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22630  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22631  * this may be omitted.</p></li>
22632  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22633  * <ul><li>auto (Default, implies no conversion)</li>
22634  * <li>string</li>
22635  * <li>int</li>
22636  * <li>float</li>
22637  * <li>boolean</li>
22638  * <li>date</li></ul></p></li>
22639  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22640  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22641  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22642  * by the Reader into an object that will be stored in the Record. It is passed the
22643  * following parameters:<ul>
22644  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22645  * </ul></p></li>
22646  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22647  * </ul>
22648  * <br>usage:<br><pre><code>
22649 var TopicRecord = Roo.data.Record.create(
22650     {name: 'title', mapping: 'topic_title'},
22651     {name: 'author', mapping: 'username'},
22652     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22653     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22654     {name: 'lastPoster', mapping: 'user2'},
22655     {name: 'excerpt', mapping: 'post_text'}
22656 );
22657
22658 var myNewRecord = new TopicRecord({
22659     title: 'Do my job please',
22660     author: 'noobie',
22661     totalPosts: 1,
22662     lastPost: new Date(),
22663     lastPoster: 'Animal',
22664     excerpt: 'No way dude!'
22665 });
22666 myStore.add(myNewRecord);
22667 </code></pre>
22668  * @method create
22669  * @static
22670  */
22671 Roo.data.Record.create = function(o){
22672     var f = function(){
22673         f.superclass.constructor.apply(this, arguments);
22674     };
22675     Roo.extend(f, Roo.data.Record);
22676     var p = f.prototype;
22677     p.fields = new Roo.util.MixedCollection(false, function(field){
22678         return field.name;
22679     });
22680     for(var i = 0, len = o.length; i < len; i++){
22681         p.fields.add(new Roo.data.Field(o[i]));
22682     }
22683     f.getField = function(name){
22684         return p.fields.get(name);  
22685     };
22686     return f;
22687 };
22688
22689 Roo.data.Record.AUTO_ID = 1000;
22690 Roo.data.Record.EDIT = 'edit';
22691 Roo.data.Record.REJECT = 'reject';
22692 Roo.data.Record.COMMIT = 'commit';
22693
22694 Roo.data.Record.prototype = {
22695     /**
22696      * Readonly flag - true if this record has been modified.
22697      * @type Boolean
22698      */
22699     dirty : false,
22700     editing : false,
22701     error: null,
22702     modified: null,
22703
22704     // private
22705     join : function(store){
22706         this.store = store;
22707     },
22708
22709     /**
22710      * Set the named field to the specified value.
22711      * @param {String} name The name of the field to set.
22712      * @param {Object} value The value to set the field to.
22713      */
22714     set : function(name, value){
22715         if(this.data[name] == value){
22716             return;
22717         }
22718         this.dirty = true;
22719         if(!this.modified){
22720             this.modified = {};
22721         }
22722         if(typeof this.modified[name] == 'undefined'){
22723             this.modified[name] = this.data[name];
22724         }
22725         this.data[name] = value;
22726         if(!this.editing && this.store){
22727             this.store.afterEdit(this);
22728         }       
22729     },
22730
22731     /**
22732      * Get the value of the named field.
22733      * @param {String} name The name of the field to get the value of.
22734      * @return {Object} The value of the field.
22735      */
22736     get : function(name){
22737         return this.data[name]; 
22738     },
22739
22740     // private
22741     beginEdit : function(){
22742         this.editing = true;
22743         this.modified = {}; 
22744     },
22745
22746     // private
22747     cancelEdit : function(){
22748         this.editing = false;
22749         delete this.modified;
22750     },
22751
22752     // private
22753     endEdit : function(){
22754         this.editing = false;
22755         if(this.dirty && this.store){
22756             this.store.afterEdit(this);
22757         }
22758     },
22759
22760     /**
22761      * Usually called by the {@link Roo.data.Store} which owns the Record.
22762      * Rejects all changes made to the Record since either creation, or the last commit operation.
22763      * Modified fields are reverted to their original values.
22764      * <p>
22765      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22766      * of reject operations.
22767      */
22768     reject : function(){
22769         var m = this.modified;
22770         for(var n in m){
22771             if(typeof m[n] != "function"){
22772                 this.data[n] = m[n];
22773             }
22774         }
22775         this.dirty = false;
22776         delete this.modified;
22777         this.editing = false;
22778         if(this.store){
22779             this.store.afterReject(this);
22780         }
22781     },
22782
22783     /**
22784      * Usually called by the {@link Roo.data.Store} which owns the Record.
22785      * Commits all changes made to the Record since either creation, or the last commit operation.
22786      * <p>
22787      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22788      * of commit operations.
22789      */
22790     commit : function(){
22791         this.dirty = false;
22792         delete this.modified;
22793         this.editing = false;
22794         if(this.store){
22795             this.store.afterCommit(this);
22796         }
22797     },
22798
22799     // private
22800     hasError : function(){
22801         return this.error != null;
22802     },
22803
22804     // private
22805     clearError : function(){
22806         this.error = null;
22807     },
22808
22809     /**
22810      * Creates a copy of this record.
22811      * @param {String} id (optional) A new record id if you don't want to use this record's id
22812      * @return {Record}
22813      */
22814     copy : function(newId) {
22815         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22816     }
22817 };/*
22818  * Based on:
22819  * Ext JS Library 1.1.1
22820  * Copyright(c) 2006-2007, Ext JS, LLC.
22821  *
22822  * Originally Released Under LGPL - original licence link has changed is not relivant.
22823  *
22824  * Fork - LGPL
22825  * <script type="text/javascript">
22826  */
22827
22828
22829
22830 /**
22831  * @class Roo.data.Store
22832  * @extends Roo.util.Observable
22833  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22834  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22835  * <p>
22836  * 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
22837  * has no knowledge of the format of the data returned by the Proxy.<br>
22838  * <p>
22839  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22840  * instances from the data object. These records are cached and made available through accessor functions.
22841  * @constructor
22842  * Creates a new Store.
22843  * @param {Object} config A config object containing the objects needed for the Store to access data,
22844  * and read the data into Records.
22845  */
22846 Roo.data.Store = function(config){
22847     this.data = new Roo.util.MixedCollection(false);
22848     this.data.getKey = function(o){
22849         return o.id;
22850     };
22851     this.baseParams = {};
22852     // private
22853     this.paramNames = {
22854         "start" : "start",
22855         "limit" : "limit",
22856         "sort" : "sort",
22857         "dir" : "dir",
22858         "multisort" : "_multisort"
22859     };
22860
22861     if(config && config.data){
22862         this.inlineData = config.data;
22863         delete config.data;
22864     }
22865
22866     Roo.apply(this, config);
22867     
22868     if(this.reader){ // reader passed
22869         this.reader = Roo.factory(this.reader, Roo.data);
22870         this.reader.xmodule = this.xmodule || false;
22871         if(!this.recordType){
22872             this.recordType = this.reader.recordType;
22873         }
22874         if(this.reader.onMetaChange){
22875             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22876         }
22877     }
22878
22879     if(this.recordType){
22880         this.fields = this.recordType.prototype.fields;
22881     }
22882     this.modified = [];
22883
22884     this.addEvents({
22885         /**
22886          * @event datachanged
22887          * Fires when the data cache has changed, and a widget which is using this Store
22888          * as a Record cache should refresh its view.
22889          * @param {Store} this
22890          */
22891         datachanged : true,
22892         /**
22893          * @event metachange
22894          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22895          * @param {Store} this
22896          * @param {Object} meta The JSON metadata
22897          */
22898         metachange : true,
22899         /**
22900          * @event add
22901          * Fires when Records have been added to the Store
22902          * @param {Store} this
22903          * @param {Roo.data.Record[]} records The array of Records added
22904          * @param {Number} index The index at which the record(s) were added
22905          */
22906         add : true,
22907         /**
22908          * @event remove
22909          * Fires when a Record has been removed from the Store
22910          * @param {Store} this
22911          * @param {Roo.data.Record} record The Record that was removed
22912          * @param {Number} index The index at which the record was removed
22913          */
22914         remove : true,
22915         /**
22916          * @event update
22917          * Fires when a Record has been updated
22918          * @param {Store} this
22919          * @param {Roo.data.Record} record The Record that was updated
22920          * @param {String} operation The update operation being performed.  Value may be one of:
22921          * <pre><code>
22922  Roo.data.Record.EDIT
22923  Roo.data.Record.REJECT
22924  Roo.data.Record.COMMIT
22925          * </code></pre>
22926          */
22927         update : true,
22928         /**
22929          * @event clear
22930          * Fires when the data cache has been cleared.
22931          * @param {Store} this
22932          */
22933         clear : true,
22934         /**
22935          * @event beforeload
22936          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22937          * the load action will be canceled.
22938          * @param {Store} this
22939          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22940          */
22941         beforeload : true,
22942         /**
22943          * @event beforeloadadd
22944          * Fires after a new set of Records has been loaded.
22945          * @param {Store} this
22946          * @param {Roo.data.Record[]} records The Records that were loaded
22947          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22948          */
22949         beforeloadadd : true,
22950         /**
22951          * @event load
22952          * Fires after a new set of Records has been loaded, before they are added to the store.
22953          * @param {Store} this
22954          * @param {Roo.data.Record[]} records The Records that were loaded
22955          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22956          * @params {Object} return from reader
22957          */
22958         load : true,
22959         /**
22960          * @event loadexception
22961          * Fires if an exception occurs in the Proxy during loading.
22962          * Called with the signature of the Proxy's "loadexception" event.
22963          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22964          * 
22965          * @param {Proxy} 
22966          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22967          * @param {Object} load options 
22968          * @param {Object} jsonData from your request (normally this contains the Exception)
22969          */
22970         loadexception : true
22971     });
22972     
22973     if(this.proxy){
22974         this.proxy = Roo.factory(this.proxy, Roo.data);
22975         this.proxy.xmodule = this.xmodule || false;
22976         this.relayEvents(this.proxy,  ["loadexception"]);
22977     }
22978     this.sortToggle = {};
22979     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22980
22981     Roo.data.Store.superclass.constructor.call(this);
22982
22983     if(this.inlineData){
22984         this.loadData(this.inlineData);
22985         delete this.inlineData;
22986     }
22987 };
22988
22989 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22990      /**
22991     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22992     * without a remote query - used by combo/forms at present.
22993     */
22994     
22995     /**
22996     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22997     */
22998     /**
22999     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23000     */
23001     /**
23002     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
23003     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23004     */
23005     /**
23006     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23007     * on any HTTP request
23008     */
23009     /**
23010     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23011     */
23012     /**
23013     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23014     */
23015     multiSort: false,
23016     /**
23017     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23018     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23019     */
23020     remoteSort : false,
23021
23022     /**
23023     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23024      * loaded or when a record is removed. (defaults to false).
23025     */
23026     pruneModifiedRecords : false,
23027
23028     // private
23029     lastOptions : null,
23030
23031     /**
23032      * Add Records to the Store and fires the add event.
23033      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23034      */
23035     add : function(records){
23036         records = [].concat(records);
23037         for(var i = 0, len = records.length; i < len; i++){
23038             records[i].join(this);
23039         }
23040         var index = this.data.length;
23041         this.data.addAll(records);
23042         this.fireEvent("add", this, records, index);
23043     },
23044
23045     /**
23046      * Remove a Record from the Store and fires the remove event.
23047      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23048      */
23049     remove : function(record){
23050         var index = this.data.indexOf(record);
23051         this.data.removeAt(index);
23052  
23053         if(this.pruneModifiedRecords){
23054             this.modified.remove(record);
23055         }
23056         this.fireEvent("remove", this, record, index);
23057     },
23058
23059     /**
23060      * Remove all Records from the Store and fires the clear event.
23061      */
23062     removeAll : function(){
23063         this.data.clear();
23064         if(this.pruneModifiedRecords){
23065             this.modified = [];
23066         }
23067         this.fireEvent("clear", this);
23068     },
23069
23070     /**
23071      * Inserts Records to the Store at the given index and fires the add event.
23072      * @param {Number} index The start index at which to insert the passed Records.
23073      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23074      */
23075     insert : function(index, records){
23076         records = [].concat(records);
23077         for(var i = 0, len = records.length; i < len; i++){
23078             this.data.insert(index, records[i]);
23079             records[i].join(this);
23080         }
23081         this.fireEvent("add", this, records, index);
23082     },
23083
23084     /**
23085      * Get the index within the cache of the passed Record.
23086      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23087      * @return {Number} The index of the passed Record. Returns -1 if not found.
23088      */
23089     indexOf : function(record){
23090         return this.data.indexOf(record);
23091     },
23092
23093     /**
23094      * Get the index within the cache of the Record with the passed id.
23095      * @param {String} id The id of the Record to find.
23096      * @return {Number} The index of the Record. Returns -1 if not found.
23097      */
23098     indexOfId : function(id){
23099         return this.data.indexOfKey(id);
23100     },
23101
23102     /**
23103      * Get the Record with the specified id.
23104      * @param {String} id The id of the Record to find.
23105      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23106      */
23107     getById : function(id){
23108         return this.data.key(id);
23109     },
23110
23111     /**
23112      * Get the Record at the specified index.
23113      * @param {Number} index The index of the Record to find.
23114      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23115      */
23116     getAt : function(index){
23117         return this.data.itemAt(index);
23118     },
23119
23120     /**
23121      * Returns a range of Records between specified indices.
23122      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23123      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23124      * @return {Roo.data.Record[]} An array of Records
23125      */
23126     getRange : function(start, end){
23127         return this.data.getRange(start, end);
23128     },
23129
23130     // private
23131     storeOptions : function(o){
23132         o = Roo.apply({}, o);
23133         delete o.callback;
23134         delete o.scope;
23135         this.lastOptions = o;
23136     },
23137
23138     /**
23139      * Loads the Record cache from the configured Proxy using the configured Reader.
23140      * <p>
23141      * If using remote paging, then the first load call must specify the <em>start</em>
23142      * and <em>limit</em> properties in the options.params property to establish the initial
23143      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23144      * <p>
23145      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23146      * and this call will return before the new data has been loaded. Perform any post-processing
23147      * in a callback function, or in a "load" event handler.</strong>
23148      * <p>
23149      * @param {Object} options An object containing properties which control loading options:<ul>
23150      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23151      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23152      * passed the following arguments:<ul>
23153      * <li>r : Roo.data.Record[]</li>
23154      * <li>options: Options object from the load call</li>
23155      * <li>success: Boolean success indicator</li></ul></li>
23156      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23157      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23158      * </ul>
23159      */
23160     load : function(options){
23161         options = options || {};
23162         if(this.fireEvent("beforeload", this, options) !== false){
23163             this.storeOptions(options);
23164             var p = Roo.apply(options.params || {}, this.baseParams);
23165             // if meta was not loaded from remote source.. try requesting it.
23166             if (!this.reader.metaFromRemote) {
23167                 p._requestMeta = 1;
23168             }
23169             if(this.sortInfo && this.remoteSort){
23170                 var pn = this.paramNames;
23171                 p[pn["sort"]] = this.sortInfo.field;
23172                 p[pn["dir"]] = this.sortInfo.direction;
23173             }
23174             if (this.multiSort) {
23175                 var pn = this.paramNames;
23176                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23177             }
23178             
23179             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23180         }
23181     },
23182
23183     /**
23184      * Reloads the Record cache from the configured Proxy using the configured Reader and
23185      * the options from the last load operation performed.
23186      * @param {Object} options (optional) An object containing properties which may override the options
23187      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23188      * the most recently used options are reused).
23189      */
23190     reload : function(options){
23191         this.load(Roo.applyIf(options||{}, this.lastOptions));
23192     },
23193
23194     // private
23195     // Called as a callback by the Reader during a load operation.
23196     loadRecords : function(o, options, success){
23197         if(!o || success === false){
23198             if(success !== false){
23199                 this.fireEvent("load", this, [], options, o);
23200             }
23201             if(options.callback){
23202                 options.callback.call(options.scope || this, [], options, false);
23203             }
23204             return;
23205         }
23206         // if data returned failure - throw an exception.
23207         if (o.success === false) {
23208             // show a message if no listener is registered.
23209             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23210                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23211             }
23212             // loadmask wil be hooked into this..
23213             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23214             return;
23215         }
23216         var r = o.records, t = o.totalRecords || r.length;
23217         
23218         this.fireEvent("beforeloadadd", this, r, options, o);
23219         
23220         if(!options || options.add !== true){
23221             if(this.pruneModifiedRecords){
23222                 this.modified = [];
23223             }
23224             for(var i = 0, len = r.length; i < len; i++){
23225                 r[i].join(this);
23226             }
23227             if(this.snapshot){
23228                 this.data = this.snapshot;
23229                 delete this.snapshot;
23230             }
23231             this.data.clear();
23232             this.data.addAll(r);
23233             this.totalLength = t;
23234             this.applySort();
23235             this.fireEvent("datachanged", this);
23236         }else{
23237             this.totalLength = Math.max(t, this.data.length+r.length);
23238             this.add(r);
23239         }
23240         
23241         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23242                 
23243             var e = new Roo.data.Record({});
23244
23245             e.set(this.parent.displayField, this.parent.emptyTitle);
23246             e.set(this.parent.valueField, '');
23247
23248             this.insert(0, e);
23249         }
23250             
23251         this.fireEvent("load", this, r, options, o);
23252         if(options.callback){
23253             options.callback.call(options.scope || this, r, options, true);
23254         }
23255     },
23256
23257
23258     /**
23259      * Loads data from a passed data block. A Reader which understands the format of the data
23260      * must have been configured in the constructor.
23261      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23262      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23263      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23264      */
23265     loadData : function(o, append){
23266         var r = this.reader.readRecords(o);
23267         this.loadRecords(r, {add: append}, true);
23268     },
23269
23270     /**
23271      * Gets the number of cached records.
23272      * <p>
23273      * <em>If using paging, this may not be the total size of the dataset. If the data object
23274      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23275      * the data set size</em>
23276      */
23277     getCount : function(){
23278         return this.data.length || 0;
23279     },
23280
23281     /**
23282      * Gets the total number of records in the dataset as returned by the server.
23283      * <p>
23284      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23285      * the dataset size</em>
23286      */
23287     getTotalCount : function(){
23288         return this.totalLength || 0;
23289     },
23290
23291     /**
23292      * Returns the sort state of the Store as an object with two properties:
23293      * <pre><code>
23294  field {String} The name of the field by which the Records are sorted
23295  direction {String} The sort order, "ASC" or "DESC"
23296      * </code></pre>
23297      */
23298     getSortState : function(){
23299         return this.sortInfo;
23300     },
23301
23302     // private
23303     applySort : function(){
23304         if(this.sortInfo && !this.remoteSort){
23305             var s = this.sortInfo, f = s.field;
23306             var st = this.fields.get(f).sortType;
23307             var fn = function(r1, r2){
23308                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23309                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23310             };
23311             this.data.sort(s.direction, fn);
23312             if(this.snapshot && this.snapshot != this.data){
23313                 this.snapshot.sort(s.direction, fn);
23314             }
23315         }
23316     },
23317
23318     /**
23319      * Sets the default sort column and order to be used by the next load operation.
23320      * @param {String} fieldName The name of the field to sort by.
23321      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23322      */
23323     setDefaultSort : function(field, dir){
23324         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23325     },
23326
23327     /**
23328      * Sort the Records.
23329      * If remote sorting is used, the sort is performed on the server, and the cache is
23330      * reloaded. If local sorting is used, the cache is sorted internally.
23331      * @param {String} fieldName The name of the field to sort by.
23332      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23333      */
23334     sort : function(fieldName, dir){
23335         var f = this.fields.get(fieldName);
23336         if(!dir){
23337             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23338             
23339             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23340                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23341             }else{
23342                 dir = f.sortDir;
23343             }
23344         }
23345         this.sortToggle[f.name] = dir;
23346         this.sortInfo = {field: f.name, direction: dir};
23347         if(!this.remoteSort){
23348             this.applySort();
23349             this.fireEvent("datachanged", this);
23350         }else{
23351             this.load(this.lastOptions);
23352         }
23353     },
23354
23355     /**
23356      * Calls the specified function for each of the Records in the cache.
23357      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23358      * Returning <em>false</em> aborts and exits the iteration.
23359      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23360      */
23361     each : function(fn, scope){
23362         this.data.each(fn, scope);
23363     },
23364
23365     /**
23366      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23367      * (e.g., during paging).
23368      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23369      */
23370     getModifiedRecords : function(){
23371         return this.modified;
23372     },
23373
23374     // private
23375     createFilterFn : function(property, value, anyMatch){
23376         if(!value.exec){ // not a regex
23377             value = String(value);
23378             if(value.length == 0){
23379                 return false;
23380             }
23381             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23382         }
23383         return function(r){
23384             return value.test(r.data[property]);
23385         };
23386     },
23387
23388     /**
23389      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23390      * @param {String} property A field on your records
23391      * @param {Number} start The record index to start at (defaults to 0)
23392      * @param {Number} end The last record index to include (defaults to length - 1)
23393      * @return {Number} The sum
23394      */
23395     sum : function(property, start, end){
23396         var rs = this.data.items, v = 0;
23397         start = start || 0;
23398         end = (end || end === 0) ? end : rs.length-1;
23399
23400         for(var i = start; i <= end; i++){
23401             v += (rs[i].data[property] || 0);
23402         }
23403         return v;
23404     },
23405
23406     /**
23407      * Filter the records by a specified property.
23408      * @param {String} field A field on your records
23409      * @param {String/RegExp} value Either a string that the field
23410      * should start with or a RegExp to test against the field
23411      * @param {Boolean} anyMatch True to match any part not just the beginning
23412      */
23413     filter : function(property, value, anyMatch){
23414         var fn = this.createFilterFn(property, value, anyMatch);
23415         return fn ? this.filterBy(fn) : this.clearFilter();
23416     },
23417
23418     /**
23419      * Filter by a function. The specified function will be called with each
23420      * record in this data source. If the function returns true the record is included,
23421      * otherwise it is filtered.
23422      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23423      * @param {Object} scope (optional) The scope of the function (defaults to this)
23424      */
23425     filterBy : function(fn, scope){
23426         this.snapshot = this.snapshot || this.data;
23427         this.data = this.queryBy(fn, scope||this);
23428         this.fireEvent("datachanged", this);
23429     },
23430
23431     /**
23432      * Query the records by a specified property.
23433      * @param {String} field A field on your records
23434      * @param {String/RegExp} value Either a string that the field
23435      * should start with or a RegExp to test against the field
23436      * @param {Boolean} anyMatch True to match any part not just the beginning
23437      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23438      */
23439     query : function(property, value, anyMatch){
23440         var fn = this.createFilterFn(property, value, anyMatch);
23441         return fn ? this.queryBy(fn) : this.data.clone();
23442     },
23443
23444     /**
23445      * Query by a function. The specified function will be called with each
23446      * record in this data source. If the function returns true the record is included
23447      * in the results.
23448      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23449      * @param {Object} scope (optional) The scope of the function (defaults to this)
23450       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23451      **/
23452     queryBy : function(fn, scope){
23453         var data = this.snapshot || this.data;
23454         return data.filterBy(fn, scope||this);
23455     },
23456
23457     /**
23458      * Collects unique values for a particular dataIndex from this store.
23459      * @param {String} dataIndex The property to collect
23460      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23461      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23462      * @return {Array} An array of the unique values
23463      **/
23464     collect : function(dataIndex, allowNull, bypassFilter){
23465         var d = (bypassFilter === true && this.snapshot) ?
23466                 this.snapshot.items : this.data.items;
23467         var v, sv, r = [], l = {};
23468         for(var i = 0, len = d.length; i < len; i++){
23469             v = d[i].data[dataIndex];
23470             sv = String(v);
23471             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23472                 l[sv] = true;
23473                 r[r.length] = v;
23474             }
23475         }
23476         return r;
23477     },
23478
23479     /**
23480      * Revert to a view of the Record cache with no filtering applied.
23481      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23482      */
23483     clearFilter : function(suppressEvent){
23484         if(this.snapshot && this.snapshot != this.data){
23485             this.data = this.snapshot;
23486             delete this.snapshot;
23487             if(suppressEvent !== true){
23488                 this.fireEvent("datachanged", this);
23489             }
23490         }
23491     },
23492
23493     // private
23494     afterEdit : function(record){
23495         if(this.modified.indexOf(record) == -1){
23496             this.modified.push(record);
23497         }
23498         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23499     },
23500     
23501     // private
23502     afterReject : function(record){
23503         this.modified.remove(record);
23504         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23505     },
23506
23507     // private
23508     afterCommit : function(record){
23509         this.modified.remove(record);
23510         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23511     },
23512
23513     /**
23514      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23515      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23516      */
23517     commitChanges : function(){
23518         var m = this.modified.slice(0);
23519         this.modified = [];
23520         for(var i = 0, len = m.length; i < len; i++){
23521             m[i].commit();
23522         }
23523     },
23524
23525     /**
23526      * Cancel outstanding changes on all changed records.
23527      */
23528     rejectChanges : function(){
23529         var m = this.modified.slice(0);
23530         this.modified = [];
23531         for(var i = 0, len = m.length; i < len; i++){
23532             m[i].reject();
23533         }
23534     },
23535
23536     onMetaChange : function(meta, rtype, o){
23537         this.recordType = rtype;
23538         this.fields = rtype.prototype.fields;
23539         delete this.snapshot;
23540         this.sortInfo = meta.sortInfo || this.sortInfo;
23541         this.modified = [];
23542         this.fireEvent('metachange', this, this.reader.meta);
23543     },
23544     
23545     moveIndex : function(data, type)
23546     {
23547         var index = this.indexOf(data);
23548         
23549         var newIndex = index + type;
23550         
23551         this.remove(data);
23552         
23553         this.insert(newIndex, data);
23554         
23555     }
23556 });/*
23557  * Based on:
23558  * Ext JS Library 1.1.1
23559  * Copyright(c) 2006-2007, Ext JS, LLC.
23560  *
23561  * Originally Released Under LGPL - original licence link has changed is not relivant.
23562  *
23563  * Fork - LGPL
23564  * <script type="text/javascript">
23565  */
23566
23567 /**
23568  * @class Roo.data.SimpleStore
23569  * @extends Roo.data.Store
23570  * Small helper class to make creating Stores from Array data easier.
23571  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23572  * @cfg {Array} fields An array of field definition objects, or field name strings.
23573  * @cfg {Object} an existing reader (eg. copied from another store)
23574  * @cfg {Array} data The multi-dimensional array of data
23575  * @constructor
23576  * @param {Object} config
23577  */
23578 Roo.data.SimpleStore = function(config)
23579 {
23580     Roo.data.SimpleStore.superclass.constructor.call(this, {
23581         isLocal : true,
23582         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
23583                 id: config.id
23584             },
23585             Roo.data.Record.create(config.fields)
23586         ),
23587         proxy : new Roo.data.MemoryProxy(config.data)
23588     });
23589     this.load();
23590 };
23591 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23592  * Based on:
23593  * Ext JS Library 1.1.1
23594  * Copyright(c) 2006-2007, Ext JS, LLC.
23595  *
23596  * Originally Released Under LGPL - original licence link has changed is not relivant.
23597  *
23598  * Fork - LGPL
23599  * <script type="text/javascript">
23600  */
23601
23602 /**
23603 /**
23604  * @extends Roo.data.Store
23605  * @class Roo.data.JsonStore
23606  * Small helper class to make creating Stores for JSON data easier. <br/>
23607 <pre><code>
23608 var store = new Roo.data.JsonStore({
23609     url: 'get-images.php',
23610     root: 'images',
23611     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23612 });
23613 </code></pre>
23614  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23615  * JsonReader and HttpProxy (unless inline data is provided).</b>
23616  * @cfg {Array} fields An array of field definition objects, or field name strings.
23617  * @constructor
23618  * @param {Object} config
23619  */
23620 Roo.data.JsonStore = function(c){
23621     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23622         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23623         reader: new Roo.data.JsonReader(c, c.fields)
23624     }));
23625 };
23626 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23627  * Based on:
23628  * Ext JS Library 1.1.1
23629  * Copyright(c) 2006-2007, Ext JS, LLC.
23630  *
23631  * Originally Released Under LGPL - original licence link has changed is not relivant.
23632  *
23633  * Fork - LGPL
23634  * <script type="text/javascript">
23635  */
23636
23637  
23638 Roo.data.Field = function(config){
23639     if(typeof config == "string"){
23640         config = {name: config};
23641     }
23642     Roo.apply(this, config);
23643     
23644     if(!this.type){
23645         this.type = "auto";
23646     }
23647     
23648     var st = Roo.data.SortTypes;
23649     // named sortTypes are supported, here we look them up
23650     if(typeof this.sortType == "string"){
23651         this.sortType = st[this.sortType];
23652     }
23653     
23654     // set default sortType for strings and dates
23655     if(!this.sortType){
23656         switch(this.type){
23657             case "string":
23658                 this.sortType = st.asUCString;
23659                 break;
23660             case "date":
23661                 this.sortType = st.asDate;
23662                 break;
23663             default:
23664                 this.sortType = st.none;
23665         }
23666     }
23667
23668     // define once
23669     var stripRe = /[\$,%]/g;
23670
23671     // prebuilt conversion function for this field, instead of
23672     // switching every time we're reading a value
23673     if(!this.convert){
23674         var cv, dateFormat = this.dateFormat;
23675         switch(this.type){
23676             case "":
23677             case "auto":
23678             case undefined:
23679                 cv = function(v){ return v; };
23680                 break;
23681             case "string":
23682                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23683                 break;
23684             case "int":
23685                 cv = function(v){
23686                     return v !== undefined && v !== null && v !== '' ?
23687                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23688                     };
23689                 break;
23690             case "float":
23691                 cv = function(v){
23692                     return v !== undefined && v !== null && v !== '' ?
23693                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23694                     };
23695                 break;
23696             case "bool":
23697             case "boolean":
23698                 cv = function(v){ return v === true || v === "true" || v == 1; };
23699                 break;
23700             case "date":
23701                 cv = function(v){
23702                     if(!v){
23703                         return '';
23704                     }
23705                     if(v instanceof Date){
23706                         return v;
23707                     }
23708                     if(dateFormat){
23709                         if(dateFormat == "timestamp"){
23710                             return new Date(v*1000);
23711                         }
23712                         return Date.parseDate(v, dateFormat);
23713                     }
23714                     var parsed = Date.parse(v);
23715                     return parsed ? new Date(parsed) : null;
23716                 };
23717              break;
23718             
23719         }
23720         this.convert = cv;
23721     }
23722 };
23723
23724 Roo.data.Field.prototype = {
23725     dateFormat: null,
23726     defaultValue: "",
23727     mapping: null,
23728     sortType : null,
23729     sortDir : "ASC"
23730 };/*
23731  * Based on:
23732  * Ext JS Library 1.1.1
23733  * Copyright(c) 2006-2007, Ext JS, LLC.
23734  *
23735  * Originally Released Under LGPL - original licence link has changed is not relivant.
23736  *
23737  * Fork - LGPL
23738  * <script type="text/javascript">
23739  */
23740  
23741 // Base class for reading structured data from a data source.  This class is intended to be
23742 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23743
23744 /**
23745  * @class Roo.data.DataReader
23746  * Base class for reading structured data from a data source.  This class is intended to be
23747  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23748  */
23749
23750 Roo.data.DataReader = function(meta, recordType){
23751     
23752     this.meta = meta;
23753     
23754     this.recordType = recordType instanceof Array ? 
23755         Roo.data.Record.create(recordType) : recordType;
23756 };
23757
23758 Roo.data.DataReader.prototype = {
23759      /**
23760      * Create an empty record
23761      * @param {Object} data (optional) - overlay some values
23762      * @return {Roo.data.Record} record created.
23763      */
23764     newRow :  function(d) {
23765         var da =  {};
23766         this.recordType.prototype.fields.each(function(c) {
23767             switch( c.type) {
23768                 case 'int' : da[c.name] = 0; break;
23769                 case 'date' : da[c.name] = new Date(); break;
23770                 case 'float' : da[c.name] = 0.0; break;
23771                 case 'boolean' : da[c.name] = false; break;
23772                 default : da[c.name] = ""; break;
23773             }
23774             
23775         });
23776         return new this.recordType(Roo.apply(da, d));
23777     }
23778     
23779 };/*
23780  * Based on:
23781  * Ext JS Library 1.1.1
23782  * Copyright(c) 2006-2007, Ext JS, LLC.
23783  *
23784  * Originally Released Under LGPL - original licence link has changed is not relivant.
23785  *
23786  * Fork - LGPL
23787  * <script type="text/javascript">
23788  */
23789
23790 /**
23791  * @class Roo.data.DataProxy
23792  * @extends Roo.data.Observable
23793  * This class is an abstract base class for implementations which provide retrieval of
23794  * unformatted data objects.<br>
23795  * <p>
23796  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23797  * (of the appropriate type which knows how to parse the data object) to provide a block of
23798  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23799  * <p>
23800  * Custom implementations must implement the load method as described in
23801  * {@link Roo.data.HttpProxy#load}.
23802  */
23803 Roo.data.DataProxy = function(){
23804     this.addEvents({
23805         /**
23806          * @event beforeload
23807          * Fires before a network request is made to retrieve a data object.
23808          * @param {Object} This DataProxy object.
23809          * @param {Object} params The params parameter to the load function.
23810          */
23811         beforeload : true,
23812         /**
23813          * @event load
23814          * Fires before the load method's callback is called.
23815          * @param {Object} This DataProxy object.
23816          * @param {Object} o The data object.
23817          * @param {Object} arg The callback argument object passed to the load function.
23818          */
23819         load : true,
23820         /**
23821          * @event loadexception
23822          * Fires if an Exception occurs during data retrieval.
23823          * @param {Object} This DataProxy object.
23824          * @param {Object} o The data object.
23825          * @param {Object} arg The callback argument object passed to the load function.
23826          * @param {Object} e The Exception.
23827          */
23828         loadexception : true
23829     });
23830     Roo.data.DataProxy.superclass.constructor.call(this);
23831 };
23832
23833 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23834
23835     /**
23836      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23837      */
23838 /*
23839  * Based on:
23840  * Ext JS Library 1.1.1
23841  * Copyright(c) 2006-2007, Ext JS, LLC.
23842  *
23843  * Originally Released Under LGPL - original licence link has changed is not relivant.
23844  *
23845  * Fork - LGPL
23846  * <script type="text/javascript">
23847  */
23848 /**
23849  * @class Roo.data.MemoryProxy
23850  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23851  * to the Reader when its load method is called.
23852  * @constructor
23853  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23854  */
23855 Roo.data.MemoryProxy = function(data){
23856     if (data.data) {
23857         data = data.data;
23858     }
23859     Roo.data.MemoryProxy.superclass.constructor.call(this);
23860     this.data = data;
23861 };
23862
23863 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23864     
23865     /**
23866      * Load data from the requested source (in this case an in-memory
23867      * data object passed to the constructor), read the data object into
23868      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23869      * process that block using the passed callback.
23870      * @param {Object} params This parameter is not used by the MemoryProxy class.
23871      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23872      * object into a block of Roo.data.Records.
23873      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23874      * The function must be passed <ul>
23875      * <li>The Record block object</li>
23876      * <li>The "arg" argument from the load function</li>
23877      * <li>A boolean success indicator</li>
23878      * </ul>
23879      * @param {Object} scope The scope in which to call the callback
23880      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23881      */
23882     load : function(params, reader, callback, scope, arg){
23883         params = params || {};
23884         var result;
23885         try {
23886             result = reader.readRecords(params.data ? params.data :this.data);
23887         }catch(e){
23888             this.fireEvent("loadexception", this, arg, null, e);
23889             callback.call(scope, null, arg, false);
23890             return;
23891         }
23892         callback.call(scope, result, arg, true);
23893     },
23894     
23895     // private
23896     update : function(params, records){
23897         
23898     }
23899 });/*
23900  * Based on:
23901  * Ext JS Library 1.1.1
23902  * Copyright(c) 2006-2007, Ext JS, LLC.
23903  *
23904  * Originally Released Under LGPL - original licence link has changed is not relivant.
23905  *
23906  * Fork - LGPL
23907  * <script type="text/javascript">
23908  */
23909 /**
23910  * @class Roo.data.HttpProxy
23911  * @extends Roo.data.DataProxy
23912  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23913  * configured to reference a certain URL.<br><br>
23914  * <p>
23915  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23916  * from which the running page was served.<br><br>
23917  * <p>
23918  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23919  * <p>
23920  * Be aware that to enable the browser to parse an XML document, the server must set
23921  * the Content-Type header in the HTTP response to "text/xml".
23922  * @constructor
23923  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23924  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23925  * will be used to make the request.
23926  */
23927 Roo.data.HttpProxy = function(conn){
23928     Roo.data.HttpProxy.superclass.constructor.call(this);
23929     // is conn a conn config or a real conn?
23930     this.conn = conn;
23931     this.useAjax = !conn || !conn.events;
23932   
23933 };
23934
23935 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23936     // thse are take from connection...
23937     
23938     /**
23939      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23940      */
23941     /**
23942      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23943      * extra parameters to each request made by this object. (defaults to undefined)
23944      */
23945     /**
23946      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23947      *  to each request made by this object. (defaults to undefined)
23948      */
23949     /**
23950      * @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)
23951      */
23952     /**
23953      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23954      */
23955      /**
23956      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23957      * @type Boolean
23958      */
23959   
23960
23961     /**
23962      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23963      * @type Boolean
23964      */
23965     /**
23966      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23967      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23968      * a finer-grained basis than the DataProxy events.
23969      */
23970     getConnection : function(){
23971         return this.useAjax ? Roo.Ajax : this.conn;
23972     },
23973
23974     /**
23975      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23976      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23977      * process that block using the passed callback.
23978      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23979      * for the request to the remote server.
23980      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23981      * object into a block of Roo.data.Records.
23982      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23983      * The function must be passed <ul>
23984      * <li>The Record block object</li>
23985      * <li>The "arg" argument from the load function</li>
23986      * <li>A boolean success indicator</li>
23987      * </ul>
23988      * @param {Object} scope The scope in which to call the callback
23989      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23990      */
23991     load : function(params, reader, callback, scope, arg){
23992         if(this.fireEvent("beforeload", this, params) !== false){
23993             var  o = {
23994                 params : params || {},
23995                 request: {
23996                     callback : callback,
23997                     scope : scope,
23998                     arg : arg
23999                 },
24000                 reader: reader,
24001                 callback : this.loadResponse,
24002                 scope: this
24003             };
24004             if(this.useAjax){
24005                 Roo.applyIf(o, this.conn);
24006                 if(this.activeRequest){
24007                     Roo.Ajax.abort(this.activeRequest);
24008                 }
24009                 this.activeRequest = Roo.Ajax.request(o);
24010             }else{
24011                 this.conn.request(o);
24012             }
24013         }else{
24014             callback.call(scope||this, null, arg, false);
24015         }
24016     },
24017
24018     // private
24019     loadResponse : function(o, success, response){
24020         delete this.activeRequest;
24021         if(!success){
24022             this.fireEvent("loadexception", this, o, response);
24023             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24024             return;
24025         }
24026         var result;
24027         try {
24028             result = o.reader.read(response);
24029         }catch(e){
24030             this.fireEvent("loadexception", this, o, response, e);
24031             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24032             return;
24033         }
24034         
24035         this.fireEvent("load", this, o, o.request.arg);
24036         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24037     },
24038
24039     // private
24040     update : function(dataSet){
24041
24042     },
24043
24044     // private
24045     updateResponse : function(dataSet){
24046
24047     }
24048 });/*
24049  * Based on:
24050  * Ext JS Library 1.1.1
24051  * Copyright(c) 2006-2007, Ext JS, LLC.
24052  *
24053  * Originally Released Under LGPL - original licence link has changed is not relivant.
24054  *
24055  * Fork - LGPL
24056  * <script type="text/javascript">
24057  */
24058
24059 /**
24060  * @class Roo.data.ScriptTagProxy
24061  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24062  * other than the originating domain of the running page.<br><br>
24063  * <p>
24064  * <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
24065  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24066  * <p>
24067  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24068  * source code that is used as the source inside a &lt;script> tag.<br><br>
24069  * <p>
24070  * In order for the browser to process the returned data, the server must wrap the data object
24071  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24072  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24073  * depending on whether the callback name was passed:
24074  * <p>
24075  * <pre><code>
24076 boolean scriptTag = false;
24077 String cb = request.getParameter("callback");
24078 if (cb != null) {
24079     scriptTag = true;
24080     response.setContentType("text/javascript");
24081 } else {
24082     response.setContentType("application/x-json");
24083 }
24084 Writer out = response.getWriter();
24085 if (scriptTag) {
24086     out.write(cb + "(");
24087 }
24088 out.print(dataBlock.toJsonString());
24089 if (scriptTag) {
24090     out.write(");");
24091 }
24092 </pre></code>
24093  *
24094  * @constructor
24095  * @param {Object} config A configuration object.
24096  */
24097 Roo.data.ScriptTagProxy = function(config){
24098     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24099     Roo.apply(this, config);
24100     this.head = document.getElementsByTagName("head")[0];
24101 };
24102
24103 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24104
24105 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24106     /**
24107      * @cfg {String} url The URL from which to request the data object.
24108      */
24109     /**
24110      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24111      */
24112     timeout : 30000,
24113     /**
24114      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24115      * the server the name of the callback function set up by the load call to process the returned data object.
24116      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24117      * javascript output which calls this named function passing the data object as its only parameter.
24118      */
24119     callbackParam : "callback",
24120     /**
24121      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24122      * name to the request.
24123      */
24124     nocache : true,
24125
24126     /**
24127      * Load data from the configured URL, read the data object into
24128      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24129      * process that block using the passed callback.
24130      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24131      * for the request to the remote server.
24132      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24133      * object into a block of Roo.data.Records.
24134      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24135      * The function must be passed <ul>
24136      * <li>The Record block object</li>
24137      * <li>The "arg" argument from the load function</li>
24138      * <li>A boolean success indicator</li>
24139      * </ul>
24140      * @param {Object} scope The scope in which to call the callback
24141      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24142      */
24143     load : function(params, reader, callback, scope, arg){
24144         if(this.fireEvent("beforeload", this, params) !== false){
24145
24146             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24147
24148             var url = this.url;
24149             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24150             if(this.nocache){
24151                 url += "&_dc=" + (new Date().getTime());
24152             }
24153             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24154             var trans = {
24155                 id : transId,
24156                 cb : "stcCallback"+transId,
24157                 scriptId : "stcScript"+transId,
24158                 params : params,
24159                 arg : arg,
24160                 url : url,
24161                 callback : callback,
24162                 scope : scope,
24163                 reader : reader
24164             };
24165             var conn = this;
24166
24167             window[trans.cb] = function(o){
24168                 conn.handleResponse(o, trans);
24169             };
24170
24171             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24172
24173             if(this.autoAbort !== false){
24174                 this.abort();
24175             }
24176
24177             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24178
24179             var script = document.createElement("script");
24180             script.setAttribute("src", url);
24181             script.setAttribute("type", "text/javascript");
24182             script.setAttribute("id", trans.scriptId);
24183             this.head.appendChild(script);
24184
24185             this.trans = trans;
24186         }else{
24187             callback.call(scope||this, null, arg, false);
24188         }
24189     },
24190
24191     // private
24192     isLoading : function(){
24193         return this.trans ? true : false;
24194     },
24195
24196     /**
24197      * Abort the current server request.
24198      */
24199     abort : function(){
24200         if(this.isLoading()){
24201             this.destroyTrans(this.trans);
24202         }
24203     },
24204
24205     // private
24206     destroyTrans : function(trans, isLoaded){
24207         this.head.removeChild(document.getElementById(trans.scriptId));
24208         clearTimeout(trans.timeoutId);
24209         if(isLoaded){
24210             window[trans.cb] = undefined;
24211             try{
24212                 delete window[trans.cb];
24213             }catch(e){}
24214         }else{
24215             // if hasn't been loaded, wait for load to remove it to prevent script error
24216             window[trans.cb] = function(){
24217                 window[trans.cb] = undefined;
24218                 try{
24219                     delete window[trans.cb];
24220                 }catch(e){}
24221             };
24222         }
24223     },
24224
24225     // private
24226     handleResponse : function(o, trans){
24227         this.trans = false;
24228         this.destroyTrans(trans, true);
24229         var result;
24230         try {
24231             result = trans.reader.readRecords(o);
24232         }catch(e){
24233             this.fireEvent("loadexception", this, o, trans.arg, e);
24234             trans.callback.call(trans.scope||window, null, trans.arg, false);
24235             return;
24236         }
24237         this.fireEvent("load", this, o, trans.arg);
24238         trans.callback.call(trans.scope||window, result, trans.arg, true);
24239     },
24240
24241     // private
24242     handleFailure : function(trans){
24243         this.trans = false;
24244         this.destroyTrans(trans, false);
24245         this.fireEvent("loadexception", this, null, trans.arg);
24246         trans.callback.call(trans.scope||window, null, trans.arg, false);
24247     }
24248 });/*
24249  * Based on:
24250  * Ext JS Library 1.1.1
24251  * Copyright(c) 2006-2007, Ext JS, LLC.
24252  *
24253  * Originally Released Under LGPL - original licence link has changed is not relivant.
24254  *
24255  * Fork - LGPL
24256  * <script type="text/javascript">
24257  */
24258
24259 /**
24260  * @class Roo.data.JsonReader
24261  * @extends Roo.data.DataReader
24262  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24263  * based on mappings in a provided Roo.data.Record constructor.
24264  * 
24265  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24266  * in the reply previously. 
24267  * 
24268  * <p>
24269  * Example code:
24270  * <pre><code>
24271 var RecordDef = Roo.data.Record.create([
24272     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24273     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24274 ]);
24275 var myReader = new Roo.data.JsonReader({
24276     totalProperty: "results",    // The property which contains the total dataset size (optional)
24277     root: "rows",                // The property which contains an Array of row objects
24278     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24279 }, RecordDef);
24280 </code></pre>
24281  * <p>
24282  * This would consume a JSON file like this:
24283  * <pre><code>
24284 { 'results': 2, 'rows': [
24285     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24286     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24287 }
24288 </code></pre>
24289  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24290  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24291  * paged from the remote server.
24292  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24293  * @cfg {String} root name of the property which contains the Array of row objects.
24294  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24295  * @cfg {Array} fields Array of field definition objects
24296  * @constructor
24297  * Create a new JsonReader
24298  * @param {Object} meta Metadata configuration options
24299  * @param {Object} recordType Either an Array of field definition objects,
24300  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24301  */
24302 Roo.data.JsonReader = function(meta, recordType){
24303     
24304     meta = meta || {};
24305     // set some defaults:
24306     Roo.applyIf(meta, {
24307         totalProperty: 'total',
24308         successProperty : 'success',
24309         root : 'data',
24310         id : 'id'
24311     });
24312     
24313     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24314 };
24315 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24316     
24317     /**
24318      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24319      * Used by Store query builder to append _requestMeta to params.
24320      * 
24321      */
24322     metaFromRemote : false,
24323     /**
24324      * This method is only used by a DataProxy which has retrieved data from a remote server.
24325      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24326      * @return {Object} data A data block which is used by an Roo.data.Store object as
24327      * a cache of Roo.data.Records.
24328      */
24329     read : function(response){
24330         var json = response.responseText;
24331        
24332         var o = /* eval:var:o */ eval("("+json+")");
24333         if(!o) {
24334             throw {message: "JsonReader.read: Json object not found"};
24335         }
24336         
24337         if(o.metaData){
24338             
24339             delete this.ef;
24340             this.metaFromRemote = true;
24341             this.meta = o.metaData;
24342             this.recordType = Roo.data.Record.create(o.metaData.fields);
24343             this.onMetaChange(this.meta, this.recordType, o);
24344         }
24345         return this.readRecords(o);
24346     },
24347
24348     // private function a store will implement
24349     onMetaChange : function(meta, recordType, o){
24350
24351     },
24352
24353     /**
24354          * @ignore
24355          */
24356     simpleAccess: function(obj, subsc) {
24357         return obj[subsc];
24358     },
24359
24360         /**
24361          * @ignore
24362          */
24363     getJsonAccessor: function(){
24364         var re = /[\[\.]/;
24365         return function(expr) {
24366             try {
24367                 return(re.test(expr))
24368                     ? new Function("obj", "return obj." + expr)
24369                     : function(obj){
24370                         return obj[expr];
24371                     };
24372             } catch(e){}
24373             return Roo.emptyFn;
24374         };
24375     }(),
24376
24377     /**
24378      * Create a data block containing Roo.data.Records from an XML document.
24379      * @param {Object} o An object which contains an Array of row objects in the property specified
24380      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24381      * which contains the total size of the dataset.
24382      * @return {Object} data A data block which is used by an Roo.data.Store object as
24383      * a cache of Roo.data.Records.
24384      */
24385     readRecords : function(o){
24386         /**
24387          * After any data loads, the raw JSON data is available for further custom processing.
24388          * @type Object
24389          */
24390         this.o = o;
24391         var s = this.meta, Record = this.recordType,
24392             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24393
24394 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24395         if (!this.ef) {
24396             if(s.totalProperty) {
24397                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24398                 }
24399                 if(s.successProperty) {
24400                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24401                 }
24402                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24403                 if (s.id) {
24404                         var g = this.getJsonAccessor(s.id);
24405                         this.getId = function(rec) {
24406                                 var r = g(rec);  
24407                                 return (r === undefined || r === "") ? null : r;
24408                         };
24409                 } else {
24410                         this.getId = function(){return null;};
24411                 }
24412             this.ef = [];
24413             for(var jj = 0; jj < fl; jj++){
24414                 f = fi[jj];
24415                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24416                 this.ef[jj] = this.getJsonAccessor(map);
24417             }
24418         }
24419
24420         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24421         if(s.totalProperty){
24422             var vt = parseInt(this.getTotal(o), 10);
24423             if(!isNaN(vt)){
24424                 totalRecords = vt;
24425             }
24426         }
24427         if(s.successProperty){
24428             var vs = this.getSuccess(o);
24429             if(vs === false || vs === 'false'){
24430                 success = false;
24431             }
24432         }
24433         var records = [];
24434         for(var i = 0; i < c; i++){
24435                 var n = root[i];
24436             var values = {};
24437             var id = this.getId(n);
24438             for(var j = 0; j < fl; j++){
24439                 f = fi[j];
24440             var v = this.ef[j](n);
24441             if (!f.convert) {
24442                 Roo.log('missing convert for ' + f.name);
24443                 Roo.log(f);
24444                 continue;
24445             }
24446             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24447             }
24448             var record = new Record(values, id);
24449             record.json = n;
24450             records[i] = record;
24451         }
24452         return {
24453             raw : o,
24454             success : success,
24455             records : records,
24456             totalRecords : totalRecords
24457         };
24458     }
24459 });/*
24460  * Based on:
24461  * Ext JS Library 1.1.1
24462  * Copyright(c) 2006-2007, Ext JS, LLC.
24463  *
24464  * Originally Released Under LGPL - original licence link has changed is not relivant.
24465  *
24466  * Fork - LGPL
24467  * <script type="text/javascript">
24468  */
24469
24470 /**
24471  * @class Roo.data.XmlReader
24472  * @extends Roo.data.DataReader
24473  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24474  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24475  * <p>
24476  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24477  * header in the HTTP response must be set to "text/xml".</em>
24478  * <p>
24479  * Example code:
24480  * <pre><code>
24481 var RecordDef = Roo.data.Record.create([
24482    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24483    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24484 ]);
24485 var myReader = new Roo.data.XmlReader({
24486    totalRecords: "results", // The element which contains the total dataset size (optional)
24487    record: "row",           // The repeated element which contains row information
24488    id: "id"                 // The element within the row that provides an ID for the record (optional)
24489 }, RecordDef);
24490 </code></pre>
24491  * <p>
24492  * This would consume an XML file like this:
24493  * <pre><code>
24494 &lt;?xml?>
24495 &lt;dataset>
24496  &lt;results>2&lt;/results>
24497  &lt;row>
24498    &lt;id>1&lt;/id>
24499    &lt;name>Bill&lt;/name>
24500    &lt;occupation>Gardener&lt;/occupation>
24501  &lt;/row>
24502  &lt;row>
24503    &lt;id>2&lt;/id>
24504    &lt;name>Ben&lt;/name>
24505    &lt;occupation>Horticulturalist&lt;/occupation>
24506  &lt;/row>
24507 &lt;/dataset>
24508 </code></pre>
24509  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24510  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24511  * paged from the remote server.
24512  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24513  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24514  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24515  * a record identifier value.
24516  * @constructor
24517  * Create a new XmlReader
24518  * @param {Object} meta Metadata configuration options
24519  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24520  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24521  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24522  */
24523 Roo.data.XmlReader = function(meta, recordType){
24524     meta = meta || {};
24525     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24526 };
24527 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24528     /**
24529      * This method is only used by a DataProxy which has retrieved data from a remote server.
24530          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24531          * to contain a method called 'responseXML' that returns an XML document object.
24532      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24533      * a cache of Roo.data.Records.
24534      */
24535     read : function(response){
24536         var doc = response.responseXML;
24537         if(!doc) {
24538             throw {message: "XmlReader.read: XML Document not available"};
24539         }
24540         return this.readRecords(doc);
24541     },
24542
24543     /**
24544      * Create a data block containing Roo.data.Records from an XML document.
24545          * @param {Object} doc A parsed XML document.
24546      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24547      * a cache of Roo.data.Records.
24548      */
24549     readRecords : function(doc){
24550         /**
24551          * After any data loads/reads, the raw XML Document is available for further custom processing.
24552          * @type XMLDocument
24553          */
24554         this.xmlData = doc;
24555         var root = doc.documentElement || doc;
24556         var q = Roo.DomQuery;
24557         var recordType = this.recordType, fields = recordType.prototype.fields;
24558         var sid = this.meta.id;
24559         var totalRecords = 0, success = true;
24560         if(this.meta.totalRecords){
24561             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24562         }
24563         
24564         if(this.meta.success){
24565             var sv = q.selectValue(this.meta.success, root, true);
24566             success = sv !== false && sv !== 'false';
24567         }
24568         var records = [];
24569         var ns = q.select(this.meta.record, root);
24570         for(var i = 0, len = ns.length; i < len; i++) {
24571                 var n = ns[i];
24572                 var values = {};
24573                 var id = sid ? q.selectValue(sid, n) : undefined;
24574                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24575                     var f = fields.items[j];
24576                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24577                     v = f.convert(v);
24578                     values[f.name] = v;
24579                 }
24580                 var record = new recordType(values, id);
24581                 record.node = n;
24582                 records[records.length] = record;
24583             }
24584
24585             return {
24586                 success : success,
24587                 records : records,
24588                 totalRecords : totalRecords || records.length
24589             };
24590     }
24591 });/*
24592  * Based on:
24593  * Ext JS Library 1.1.1
24594  * Copyright(c) 2006-2007, Ext JS, LLC.
24595  *
24596  * Originally Released Under LGPL - original licence link has changed is not relivant.
24597  *
24598  * Fork - LGPL
24599  * <script type="text/javascript">
24600  */
24601
24602 /**
24603  * @class Roo.data.ArrayReader
24604  * @extends Roo.data.DataReader
24605  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24606  * Each element of that Array represents a row of data fields. The
24607  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24608  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24609  * <p>
24610  * Example code:.
24611  * <pre><code>
24612 var RecordDef = Roo.data.Record.create([
24613     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24614     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24615 ]);
24616 var myReader = new Roo.data.ArrayReader({
24617     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24618 }, RecordDef);
24619 </code></pre>
24620  * <p>
24621  * This would consume an Array like this:
24622  * <pre><code>
24623 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24624   </code></pre>
24625  
24626  * @constructor
24627  * Create a new JsonReader
24628  * @param {Object} meta Metadata configuration options.
24629  * @param {Object|Array} recordType Either an Array of field definition objects
24630  * 
24631  * @cfg {Array} fields Array of field definition objects
24632  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24633  * as specified to {@link Roo.data.Record#create},
24634  * or an {@link Roo.data.Record} object
24635  *
24636  * 
24637  * created using {@link Roo.data.Record#create}.
24638  */
24639 Roo.data.ArrayReader = function(meta, recordType){
24640     
24641      
24642     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24643 };
24644
24645 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24646     /**
24647      * Create a data block containing Roo.data.Records from an XML document.
24648      * @param {Object} o An Array of row objects which represents the dataset.
24649      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
24650      * a cache of Roo.data.Records.
24651      */
24652     readRecords : function(o)
24653     {
24654         var sid = this.meta ? this.meta.id : null;
24655         var recordType = this.recordType, fields = recordType.prototype.fields;
24656         var records = [];
24657         var root = o;
24658         for(var i = 0; i < root.length; i++){
24659                 var n = root[i];
24660             var values = {};
24661             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24662             for(var j = 0, jlen = fields.length; j < jlen; j++){
24663                 var f = fields.items[j];
24664                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24665                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24666                 v = f.convert(v);
24667                 values[f.name] = v;
24668             }
24669             var record = new recordType(values, id);
24670             record.json = n;
24671             records[records.length] = record;
24672         }
24673         return {
24674             records : records,
24675             totalRecords : records.length
24676         };
24677     }
24678 });/*
24679  * Based on:
24680  * Ext JS Library 1.1.1
24681  * Copyright(c) 2006-2007, Ext JS, LLC.
24682  *
24683  * Originally Released Under LGPL - original licence link has changed is not relivant.
24684  *
24685  * Fork - LGPL
24686  * <script type="text/javascript">
24687  */
24688
24689
24690 /**
24691  * @class Roo.data.Tree
24692  * @extends Roo.util.Observable
24693  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24694  * in the tree have most standard DOM functionality.
24695  * @constructor
24696  * @param {Node} root (optional) The root node
24697  */
24698 Roo.data.Tree = function(root){
24699    this.nodeHash = {};
24700    /**
24701     * The root node for this tree
24702     * @type Node
24703     */
24704    this.root = null;
24705    if(root){
24706        this.setRootNode(root);
24707    }
24708    this.addEvents({
24709        /**
24710         * @event append
24711         * Fires when a new child node is appended to a node in this tree.
24712         * @param {Tree} tree The owner tree
24713         * @param {Node} parent The parent node
24714         * @param {Node} node The newly appended node
24715         * @param {Number} index The index of the newly appended node
24716         */
24717        "append" : true,
24718        /**
24719         * @event remove
24720         * Fires when a child node is removed from a node in this tree.
24721         * @param {Tree} tree The owner tree
24722         * @param {Node} parent The parent node
24723         * @param {Node} node The child node removed
24724         */
24725        "remove" : true,
24726        /**
24727         * @event move
24728         * Fires when a node is moved to a new location in the tree
24729         * @param {Tree} tree The owner tree
24730         * @param {Node} node The node moved
24731         * @param {Node} oldParent The old parent of this node
24732         * @param {Node} newParent The new parent of this node
24733         * @param {Number} index The index it was moved to
24734         */
24735        "move" : true,
24736        /**
24737         * @event insert
24738         * Fires when a new child node is inserted in a node in this tree.
24739         * @param {Tree} tree The owner tree
24740         * @param {Node} parent The parent node
24741         * @param {Node} node The child node inserted
24742         * @param {Node} refNode The child node the node was inserted before
24743         */
24744        "insert" : true,
24745        /**
24746         * @event beforeappend
24747         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24748         * @param {Tree} tree The owner tree
24749         * @param {Node} parent The parent node
24750         * @param {Node} node The child node to be appended
24751         */
24752        "beforeappend" : true,
24753        /**
24754         * @event beforeremove
24755         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24756         * @param {Tree} tree The owner tree
24757         * @param {Node} parent The parent node
24758         * @param {Node} node The child node to be removed
24759         */
24760        "beforeremove" : true,
24761        /**
24762         * @event beforemove
24763         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24764         * @param {Tree} tree The owner tree
24765         * @param {Node} node The node being moved
24766         * @param {Node} oldParent The parent of the node
24767         * @param {Node} newParent The new parent the node is moving to
24768         * @param {Number} index The index it is being moved to
24769         */
24770        "beforemove" : true,
24771        /**
24772         * @event beforeinsert
24773         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24774         * @param {Tree} tree The owner tree
24775         * @param {Node} parent The parent node
24776         * @param {Node} node The child node to be inserted
24777         * @param {Node} refNode The child node the node is being inserted before
24778         */
24779        "beforeinsert" : true
24780    });
24781
24782     Roo.data.Tree.superclass.constructor.call(this);
24783 };
24784
24785 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24786     pathSeparator: "/",
24787
24788     proxyNodeEvent : function(){
24789         return this.fireEvent.apply(this, arguments);
24790     },
24791
24792     /**
24793      * Returns the root node for this tree.
24794      * @return {Node}
24795      */
24796     getRootNode : function(){
24797         return this.root;
24798     },
24799
24800     /**
24801      * Sets the root node for this tree.
24802      * @param {Node} node
24803      * @return {Node}
24804      */
24805     setRootNode : function(node){
24806         this.root = node;
24807         node.ownerTree = this;
24808         node.isRoot = true;
24809         this.registerNode(node);
24810         return node;
24811     },
24812
24813     /**
24814      * Gets a node in this tree by its id.
24815      * @param {String} id
24816      * @return {Node}
24817      */
24818     getNodeById : function(id){
24819         return this.nodeHash[id];
24820     },
24821
24822     registerNode : function(node){
24823         this.nodeHash[node.id] = node;
24824     },
24825
24826     unregisterNode : function(node){
24827         delete this.nodeHash[node.id];
24828     },
24829
24830     toString : function(){
24831         return "[Tree"+(this.id?" "+this.id:"")+"]";
24832     }
24833 });
24834
24835 /**
24836  * @class Roo.data.Node
24837  * @extends Roo.util.Observable
24838  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24839  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24840  * @constructor
24841  * @param {Object} attributes The attributes/config for the node
24842  */
24843 Roo.data.Node = function(attributes){
24844     /**
24845      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24846      * @type {Object}
24847      */
24848     this.attributes = attributes || {};
24849     this.leaf = this.attributes.leaf;
24850     /**
24851      * The node id. @type String
24852      */
24853     this.id = this.attributes.id;
24854     if(!this.id){
24855         this.id = Roo.id(null, "ynode-");
24856         this.attributes.id = this.id;
24857     }
24858      
24859     
24860     /**
24861      * All child nodes of this node. @type Array
24862      */
24863     this.childNodes = [];
24864     if(!this.childNodes.indexOf){ // indexOf is a must
24865         this.childNodes.indexOf = function(o){
24866             for(var i = 0, len = this.length; i < len; i++){
24867                 if(this[i] == o) {
24868                     return i;
24869                 }
24870             }
24871             return -1;
24872         };
24873     }
24874     /**
24875      * The parent node for this node. @type Node
24876      */
24877     this.parentNode = null;
24878     /**
24879      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24880      */
24881     this.firstChild = null;
24882     /**
24883      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24884      */
24885     this.lastChild = null;
24886     /**
24887      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24888      */
24889     this.previousSibling = null;
24890     /**
24891      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24892      */
24893     this.nextSibling = null;
24894
24895     this.addEvents({
24896        /**
24897         * @event append
24898         * Fires when a new child node is appended
24899         * @param {Tree} tree The owner tree
24900         * @param {Node} this This node
24901         * @param {Node} node The newly appended node
24902         * @param {Number} index The index of the newly appended node
24903         */
24904        "append" : true,
24905        /**
24906         * @event remove
24907         * Fires when a child node is removed
24908         * @param {Tree} tree The owner tree
24909         * @param {Node} this This node
24910         * @param {Node} node The removed node
24911         */
24912        "remove" : true,
24913        /**
24914         * @event move
24915         * Fires when this node is moved to a new location in the tree
24916         * @param {Tree} tree The owner tree
24917         * @param {Node} this This node
24918         * @param {Node} oldParent The old parent of this node
24919         * @param {Node} newParent The new parent of this node
24920         * @param {Number} index The index it was moved to
24921         */
24922        "move" : true,
24923        /**
24924         * @event insert
24925         * Fires when a new child node is inserted.
24926         * @param {Tree} tree The owner tree
24927         * @param {Node} this This node
24928         * @param {Node} node The child node inserted
24929         * @param {Node} refNode The child node the node was inserted before
24930         */
24931        "insert" : true,
24932        /**
24933         * @event beforeappend
24934         * Fires before a new child is appended, return false to cancel the append.
24935         * @param {Tree} tree The owner tree
24936         * @param {Node} this This node
24937         * @param {Node} node The child node to be appended
24938         */
24939        "beforeappend" : true,
24940        /**
24941         * @event beforeremove
24942         * Fires before a child is removed, return false to cancel the remove.
24943         * @param {Tree} tree The owner tree
24944         * @param {Node} this This node
24945         * @param {Node} node The child node to be removed
24946         */
24947        "beforeremove" : true,
24948        /**
24949         * @event beforemove
24950         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24951         * @param {Tree} tree The owner tree
24952         * @param {Node} this This node
24953         * @param {Node} oldParent The parent of this node
24954         * @param {Node} newParent The new parent this node is moving to
24955         * @param {Number} index The index it is being moved to
24956         */
24957        "beforemove" : true,
24958        /**
24959         * @event beforeinsert
24960         * Fires before a new child is inserted, return false to cancel the insert.
24961         * @param {Tree} tree The owner tree
24962         * @param {Node} this This node
24963         * @param {Node} node The child node to be inserted
24964         * @param {Node} refNode The child node the node is being inserted before
24965         */
24966        "beforeinsert" : true
24967    });
24968     this.listeners = this.attributes.listeners;
24969     Roo.data.Node.superclass.constructor.call(this);
24970 };
24971
24972 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24973     fireEvent : function(evtName){
24974         // first do standard event for this node
24975         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24976             return false;
24977         }
24978         // then bubble it up to the tree if the event wasn't cancelled
24979         var ot = this.getOwnerTree();
24980         if(ot){
24981             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24982                 return false;
24983             }
24984         }
24985         return true;
24986     },
24987
24988     /**
24989      * Returns true if this node is a leaf
24990      * @return {Boolean}
24991      */
24992     isLeaf : function(){
24993         return this.leaf === true;
24994     },
24995
24996     // private
24997     setFirstChild : function(node){
24998         this.firstChild = node;
24999     },
25000
25001     //private
25002     setLastChild : function(node){
25003         this.lastChild = node;
25004     },
25005
25006
25007     /**
25008      * Returns true if this node is the last child of its parent
25009      * @return {Boolean}
25010      */
25011     isLast : function(){
25012        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25013     },
25014
25015     /**
25016      * Returns true if this node is the first child of its parent
25017      * @return {Boolean}
25018      */
25019     isFirst : function(){
25020        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25021     },
25022
25023     hasChildNodes : function(){
25024         return !this.isLeaf() && this.childNodes.length > 0;
25025     },
25026
25027     /**
25028      * Insert node(s) as the last child node of this node.
25029      * @param {Node/Array} node The node or Array of nodes to append
25030      * @return {Node} The appended node if single append, or null if an array was passed
25031      */
25032     appendChild : function(node){
25033         var multi = false;
25034         if(node instanceof Array){
25035             multi = node;
25036         }else if(arguments.length > 1){
25037             multi = arguments;
25038         }
25039         
25040         // if passed an array or multiple args do them one by one
25041         if(multi){
25042             for(var i = 0, len = multi.length; i < len; i++) {
25043                 this.appendChild(multi[i]);
25044             }
25045         }else{
25046             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25047                 return false;
25048             }
25049             var index = this.childNodes.length;
25050             var oldParent = node.parentNode;
25051             // it's a move, make sure we move it cleanly
25052             if(oldParent){
25053                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25054                     return false;
25055                 }
25056                 oldParent.removeChild(node);
25057             }
25058             
25059             index = this.childNodes.length;
25060             if(index == 0){
25061                 this.setFirstChild(node);
25062             }
25063             this.childNodes.push(node);
25064             node.parentNode = this;
25065             var ps = this.childNodes[index-1];
25066             if(ps){
25067                 node.previousSibling = ps;
25068                 ps.nextSibling = node;
25069             }else{
25070                 node.previousSibling = null;
25071             }
25072             node.nextSibling = null;
25073             this.setLastChild(node);
25074             node.setOwnerTree(this.getOwnerTree());
25075             this.fireEvent("append", this.ownerTree, this, node, index);
25076             if(this.ownerTree) {
25077                 this.ownerTree.fireEvent("appendnode", this, node, index);
25078             }
25079             if(oldParent){
25080                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25081             }
25082             return node;
25083         }
25084     },
25085
25086     /**
25087      * Removes a child node from this node.
25088      * @param {Node} node The node to remove
25089      * @return {Node} The removed node
25090      */
25091     removeChild : function(node){
25092         var index = this.childNodes.indexOf(node);
25093         if(index == -1){
25094             return false;
25095         }
25096         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25097             return false;
25098         }
25099
25100         // remove it from childNodes collection
25101         this.childNodes.splice(index, 1);
25102
25103         // update siblings
25104         if(node.previousSibling){
25105             node.previousSibling.nextSibling = node.nextSibling;
25106         }
25107         if(node.nextSibling){
25108             node.nextSibling.previousSibling = node.previousSibling;
25109         }
25110
25111         // update child refs
25112         if(this.firstChild == node){
25113             this.setFirstChild(node.nextSibling);
25114         }
25115         if(this.lastChild == node){
25116             this.setLastChild(node.previousSibling);
25117         }
25118
25119         node.setOwnerTree(null);
25120         // clear any references from the node
25121         node.parentNode = null;
25122         node.previousSibling = null;
25123         node.nextSibling = null;
25124         this.fireEvent("remove", this.ownerTree, this, node);
25125         return node;
25126     },
25127
25128     /**
25129      * Inserts the first node before the second node in this nodes childNodes collection.
25130      * @param {Node} node The node to insert
25131      * @param {Node} refNode The node to insert before (if null the node is appended)
25132      * @return {Node} The inserted node
25133      */
25134     insertBefore : function(node, refNode){
25135         if(!refNode){ // like standard Dom, refNode can be null for append
25136             return this.appendChild(node);
25137         }
25138         // nothing to do
25139         if(node == refNode){
25140             return false;
25141         }
25142
25143         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25144             return false;
25145         }
25146         var index = this.childNodes.indexOf(refNode);
25147         var oldParent = node.parentNode;
25148         var refIndex = index;
25149
25150         // when moving internally, indexes will change after remove
25151         if(oldParent == this && this.childNodes.indexOf(node) < index){
25152             refIndex--;
25153         }
25154
25155         // it's a move, make sure we move it cleanly
25156         if(oldParent){
25157             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25158                 return false;
25159             }
25160             oldParent.removeChild(node);
25161         }
25162         if(refIndex == 0){
25163             this.setFirstChild(node);
25164         }
25165         this.childNodes.splice(refIndex, 0, node);
25166         node.parentNode = this;
25167         var ps = this.childNodes[refIndex-1];
25168         if(ps){
25169             node.previousSibling = ps;
25170             ps.nextSibling = node;
25171         }else{
25172             node.previousSibling = null;
25173         }
25174         node.nextSibling = refNode;
25175         refNode.previousSibling = node;
25176         node.setOwnerTree(this.getOwnerTree());
25177         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25178         if(oldParent){
25179             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25180         }
25181         return node;
25182     },
25183
25184     /**
25185      * Returns the child node at the specified index.
25186      * @param {Number} index
25187      * @return {Node}
25188      */
25189     item : function(index){
25190         return this.childNodes[index];
25191     },
25192
25193     /**
25194      * Replaces one child node in this node with another.
25195      * @param {Node} newChild The replacement node
25196      * @param {Node} oldChild The node to replace
25197      * @return {Node} The replaced node
25198      */
25199     replaceChild : function(newChild, oldChild){
25200         this.insertBefore(newChild, oldChild);
25201         this.removeChild(oldChild);
25202         return oldChild;
25203     },
25204
25205     /**
25206      * Returns the index of a child node
25207      * @param {Node} node
25208      * @return {Number} The index of the node or -1 if it was not found
25209      */
25210     indexOf : function(child){
25211         return this.childNodes.indexOf(child);
25212     },
25213
25214     /**
25215      * Returns the tree this node is in.
25216      * @return {Tree}
25217      */
25218     getOwnerTree : function(){
25219         // if it doesn't have one, look for one
25220         if(!this.ownerTree){
25221             var p = this;
25222             while(p){
25223                 if(p.ownerTree){
25224                     this.ownerTree = p.ownerTree;
25225                     break;
25226                 }
25227                 p = p.parentNode;
25228             }
25229         }
25230         return this.ownerTree;
25231     },
25232
25233     /**
25234      * Returns depth of this node (the root node has a depth of 0)
25235      * @return {Number}
25236      */
25237     getDepth : function(){
25238         var depth = 0;
25239         var p = this;
25240         while(p.parentNode){
25241             ++depth;
25242             p = p.parentNode;
25243         }
25244         return depth;
25245     },
25246
25247     // private
25248     setOwnerTree : function(tree){
25249         // if it's move, we need to update everyone
25250         if(tree != this.ownerTree){
25251             if(this.ownerTree){
25252                 this.ownerTree.unregisterNode(this);
25253             }
25254             this.ownerTree = tree;
25255             var cs = this.childNodes;
25256             for(var i = 0, len = cs.length; i < len; i++) {
25257                 cs[i].setOwnerTree(tree);
25258             }
25259             if(tree){
25260                 tree.registerNode(this);
25261             }
25262         }
25263     },
25264
25265     /**
25266      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25267      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25268      * @return {String} The path
25269      */
25270     getPath : function(attr){
25271         attr = attr || "id";
25272         var p = this.parentNode;
25273         var b = [this.attributes[attr]];
25274         while(p){
25275             b.unshift(p.attributes[attr]);
25276             p = p.parentNode;
25277         }
25278         var sep = this.getOwnerTree().pathSeparator;
25279         return sep + b.join(sep);
25280     },
25281
25282     /**
25283      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25284      * function call will be the scope provided or the current node. The arguments to the function
25285      * will be the args provided or the current node. If the function returns false at any point,
25286      * the bubble is stopped.
25287      * @param {Function} fn The function to call
25288      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25289      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25290      */
25291     bubble : function(fn, scope, args){
25292         var p = this;
25293         while(p){
25294             if(fn.call(scope || p, args || p) === false){
25295                 break;
25296             }
25297             p = p.parentNode;
25298         }
25299     },
25300
25301     /**
25302      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25303      * function call will be the scope provided or the current node. The arguments to the function
25304      * will be the args provided or the current node. If the function returns false at any point,
25305      * the cascade is stopped on that branch.
25306      * @param {Function} fn The function to call
25307      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25308      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25309      */
25310     cascade : function(fn, scope, args){
25311         if(fn.call(scope || this, args || this) !== false){
25312             var cs = this.childNodes;
25313             for(var i = 0, len = cs.length; i < len; i++) {
25314                 cs[i].cascade(fn, scope, args);
25315             }
25316         }
25317     },
25318
25319     /**
25320      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25321      * function call will be the scope provided or the current node. The arguments to the function
25322      * will be the args provided or the current node. If the function returns false at any point,
25323      * the iteration stops.
25324      * @param {Function} fn The function to call
25325      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25326      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25327      */
25328     eachChild : function(fn, scope, args){
25329         var cs = this.childNodes;
25330         for(var i = 0, len = cs.length; i < len; i++) {
25331                 if(fn.call(scope || this, args || cs[i]) === false){
25332                     break;
25333                 }
25334         }
25335     },
25336
25337     /**
25338      * Finds the first child that has the attribute with the specified value.
25339      * @param {String} attribute The attribute name
25340      * @param {Mixed} value The value to search for
25341      * @return {Node} The found child or null if none was found
25342      */
25343     findChild : function(attribute, value){
25344         var cs = this.childNodes;
25345         for(var i = 0, len = cs.length; i < len; i++) {
25346                 if(cs[i].attributes[attribute] == value){
25347                     return cs[i];
25348                 }
25349         }
25350         return null;
25351     },
25352
25353     /**
25354      * Finds the first child by a custom function. The child matches if the function passed
25355      * returns true.
25356      * @param {Function} fn
25357      * @param {Object} scope (optional)
25358      * @return {Node} The found child or null if none was found
25359      */
25360     findChildBy : function(fn, scope){
25361         var cs = this.childNodes;
25362         for(var i = 0, len = cs.length; i < len; i++) {
25363                 if(fn.call(scope||cs[i], cs[i]) === true){
25364                     return cs[i];
25365                 }
25366         }
25367         return null;
25368     },
25369
25370     /**
25371      * Sorts this nodes children using the supplied sort function
25372      * @param {Function} fn
25373      * @param {Object} scope (optional)
25374      */
25375     sort : function(fn, scope){
25376         var cs = this.childNodes;
25377         var len = cs.length;
25378         if(len > 0){
25379             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25380             cs.sort(sortFn);
25381             for(var i = 0; i < len; i++){
25382                 var n = cs[i];
25383                 n.previousSibling = cs[i-1];
25384                 n.nextSibling = cs[i+1];
25385                 if(i == 0){
25386                     this.setFirstChild(n);
25387                 }
25388                 if(i == len-1){
25389                     this.setLastChild(n);
25390                 }
25391             }
25392         }
25393     },
25394
25395     /**
25396      * Returns true if this node is an ancestor (at any point) of the passed node.
25397      * @param {Node} node
25398      * @return {Boolean}
25399      */
25400     contains : function(node){
25401         return node.isAncestor(this);
25402     },
25403
25404     /**
25405      * Returns true if the passed node is an ancestor (at any point) of this node.
25406      * @param {Node} node
25407      * @return {Boolean}
25408      */
25409     isAncestor : function(node){
25410         var p = this.parentNode;
25411         while(p){
25412             if(p == node){
25413                 return true;
25414             }
25415             p = p.parentNode;
25416         }
25417         return false;
25418     },
25419
25420     toString : function(){
25421         return "[Node"+(this.id?" "+this.id:"")+"]";
25422     }
25423 });/*
25424  * Based on:
25425  * Ext JS Library 1.1.1
25426  * Copyright(c) 2006-2007, Ext JS, LLC.
25427  *
25428  * Originally Released Under LGPL - original licence link has changed is not relivant.
25429  *
25430  * Fork - LGPL
25431  * <script type="text/javascript">
25432  */
25433  (function(){ 
25434 /**
25435  * @class Roo.Layer
25436  * @extends Roo.Element
25437  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25438  * automatic maintaining of shadow/shim positions.
25439  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25440  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25441  * you can pass a string with a CSS class name. False turns off the shadow.
25442  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25443  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25444  * @cfg {String} cls CSS class to add to the element
25445  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25446  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25447  * @constructor
25448  * @param {Object} config An object with config options.
25449  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25450  */
25451
25452 Roo.Layer = function(config, existingEl){
25453     config = config || {};
25454     var dh = Roo.DomHelper;
25455     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25456     if(existingEl){
25457         this.dom = Roo.getDom(existingEl);
25458     }
25459     if(!this.dom){
25460         var o = config.dh || {tag: "div", cls: "x-layer"};
25461         this.dom = dh.append(pel, o);
25462     }
25463     if(config.cls){
25464         this.addClass(config.cls);
25465     }
25466     this.constrain = config.constrain !== false;
25467     this.visibilityMode = Roo.Element.VISIBILITY;
25468     if(config.id){
25469         this.id = this.dom.id = config.id;
25470     }else{
25471         this.id = Roo.id(this.dom);
25472     }
25473     this.zindex = config.zindex || this.getZIndex();
25474     this.position("absolute", this.zindex);
25475     if(config.shadow){
25476         this.shadowOffset = config.shadowOffset || 4;
25477         this.shadow = new Roo.Shadow({
25478             offset : this.shadowOffset,
25479             mode : config.shadow
25480         });
25481     }else{
25482         this.shadowOffset = 0;
25483     }
25484     this.useShim = config.shim !== false && Roo.useShims;
25485     this.useDisplay = config.useDisplay;
25486     this.hide();
25487 };
25488
25489 var supr = Roo.Element.prototype;
25490
25491 // shims are shared among layer to keep from having 100 iframes
25492 var shims = [];
25493
25494 Roo.extend(Roo.Layer, Roo.Element, {
25495
25496     getZIndex : function(){
25497         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25498     },
25499
25500     getShim : function(){
25501         if(!this.useShim){
25502             return null;
25503         }
25504         if(this.shim){
25505             return this.shim;
25506         }
25507         var shim = shims.shift();
25508         if(!shim){
25509             shim = this.createShim();
25510             shim.enableDisplayMode('block');
25511             shim.dom.style.display = 'none';
25512             shim.dom.style.visibility = 'visible';
25513         }
25514         var pn = this.dom.parentNode;
25515         if(shim.dom.parentNode != pn){
25516             pn.insertBefore(shim.dom, this.dom);
25517         }
25518         shim.setStyle('z-index', this.getZIndex()-2);
25519         this.shim = shim;
25520         return shim;
25521     },
25522
25523     hideShim : function(){
25524         if(this.shim){
25525             this.shim.setDisplayed(false);
25526             shims.push(this.shim);
25527             delete this.shim;
25528         }
25529     },
25530
25531     disableShadow : function(){
25532         if(this.shadow){
25533             this.shadowDisabled = true;
25534             this.shadow.hide();
25535             this.lastShadowOffset = this.shadowOffset;
25536             this.shadowOffset = 0;
25537         }
25538     },
25539
25540     enableShadow : function(show){
25541         if(this.shadow){
25542             this.shadowDisabled = false;
25543             this.shadowOffset = this.lastShadowOffset;
25544             delete this.lastShadowOffset;
25545             if(show){
25546                 this.sync(true);
25547             }
25548         }
25549     },
25550
25551     // private
25552     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25553     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25554     sync : function(doShow){
25555         var sw = this.shadow;
25556         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25557             var sh = this.getShim();
25558
25559             var w = this.getWidth(),
25560                 h = this.getHeight();
25561
25562             var l = this.getLeft(true),
25563                 t = this.getTop(true);
25564
25565             if(sw && !this.shadowDisabled){
25566                 if(doShow && !sw.isVisible()){
25567                     sw.show(this);
25568                 }else{
25569                     sw.realign(l, t, w, h);
25570                 }
25571                 if(sh){
25572                     if(doShow){
25573                        sh.show();
25574                     }
25575                     // fit the shim behind the shadow, so it is shimmed too
25576                     var a = sw.adjusts, s = sh.dom.style;
25577                     s.left = (Math.min(l, l+a.l))+"px";
25578                     s.top = (Math.min(t, t+a.t))+"px";
25579                     s.width = (w+a.w)+"px";
25580                     s.height = (h+a.h)+"px";
25581                 }
25582             }else if(sh){
25583                 if(doShow){
25584                    sh.show();
25585                 }
25586                 sh.setSize(w, h);
25587                 sh.setLeftTop(l, t);
25588             }
25589             
25590         }
25591     },
25592
25593     // private
25594     destroy : function(){
25595         this.hideShim();
25596         if(this.shadow){
25597             this.shadow.hide();
25598         }
25599         this.removeAllListeners();
25600         var pn = this.dom.parentNode;
25601         if(pn){
25602             pn.removeChild(this.dom);
25603         }
25604         Roo.Element.uncache(this.id);
25605     },
25606
25607     remove : function(){
25608         this.destroy();
25609     },
25610
25611     // private
25612     beginUpdate : function(){
25613         this.updating = true;
25614     },
25615
25616     // private
25617     endUpdate : function(){
25618         this.updating = false;
25619         this.sync(true);
25620     },
25621
25622     // private
25623     hideUnders : function(negOffset){
25624         if(this.shadow){
25625             this.shadow.hide();
25626         }
25627         this.hideShim();
25628     },
25629
25630     // private
25631     constrainXY : function(){
25632         if(this.constrain){
25633             var vw = Roo.lib.Dom.getViewWidth(),
25634                 vh = Roo.lib.Dom.getViewHeight();
25635             var s = Roo.get(document).getScroll();
25636
25637             var xy = this.getXY();
25638             var x = xy[0], y = xy[1];   
25639             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25640             // only move it if it needs it
25641             var moved = false;
25642             // first validate right/bottom
25643             if((x + w) > vw+s.left){
25644                 x = vw - w - this.shadowOffset;
25645                 moved = true;
25646             }
25647             if((y + h) > vh+s.top){
25648                 y = vh - h - this.shadowOffset;
25649                 moved = true;
25650             }
25651             // then make sure top/left isn't negative
25652             if(x < s.left){
25653                 x = s.left;
25654                 moved = true;
25655             }
25656             if(y < s.top){
25657                 y = s.top;
25658                 moved = true;
25659             }
25660             if(moved){
25661                 if(this.avoidY){
25662                     var ay = this.avoidY;
25663                     if(y <= ay && (y+h) >= ay){
25664                         y = ay-h-5;   
25665                     }
25666                 }
25667                 xy = [x, y];
25668                 this.storeXY(xy);
25669                 supr.setXY.call(this, xy);
25670                 this.sync();
25671             }
25672         }
25673     },
25674
25675     isVisible : function(){
25676         return this.visible;    
25677     },
25678
25679     // private
25680     showAction : function(){
25681         this.visible = true; // track visibility to prevent getStyle calls
25682         if(this.useDisplay === true){
25683             this.setDisplayed("");
25684         }else if(this.lastXY){
25685             supr.setXY.call(this, this.lastXY);
25686         }else if(this.lastLT){
25687             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25688         }
25689     },
25690
25691     // private
25692     hideAction : function(){
25693         this.visible = false;
25694         if(this.useDisplay === true){
25695             this.setDisplayed(false);
25696         }else{
25697             this.setLeftTop(-10000,-10000);
25698         }
25699     },
25700
25701     // overridden Element method
25702     setVisible : function(v, a, d, c, e){
25703         if(v){
25704             this.showAction();
25705         }
25706         if(a && v){
25707             var cb = function(){
25708                 this.sync(true);
25709                 if(c){
25710                     c();
25711                 }
25712             }.createDelegate(this);
25713             supr.setVisible.call(this, true, true, d, cb, e);
25714         }else{
25715             if(!v){
25716                 this.hideUnders(true);
25717             }
25718             var cb = c;
25719             if(a){
25720                 cb = function(){
25721                     this.hideAction();
25722                     if(c){
25723                         c();
25724                     }
25725                 }.createDelegate(this);
25726             }
25727             supr.setVisible.call(this, v, a, d, cb, e);
25728             if(v){
25729                 this.sync(true);
25730             }else if(!a){
25731                 this.hideAction();
25732             }
25733         }
25734     },
25735
25736     storeXY : function(xy){
25737         delete this.lastLT;
25738         this.lastXY = xy;
25739     },
25740
25741     storeLeftTop : function(left, top){
25742         delete this.lastXY;
25743         this.lastLT = [left, top];
25744     },
25745
25746     // private
25747     beforeFx : function(){
25748         this.beforeAction();
25749         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25750     },
25751
25752     // private
25753     afterFx : function(){
25754         Roo.Layer.superclass.afterFx.apply(this, arguments);
25755         this.sync(this.isVisible());
25756     },
25757
25758     // private
25759     beforeAction : function(){
25760         if(!this.updating && this.shadow){
25761             this.shadow.hide();
25762         }
25763     },
25764
25765     // overridden Element method
25766     setLeft : function(left){
25767         this.storeLeftTop(left, this.getTop(true));
25768         supr.setLeft.apply(this, arguments);
25769         this.sync();
25770     },
25771
25772     setTop : function(top){
25773         this.storeLeftTop(this.getLeft(true), top);
25774         supr.setTop.apply(this, arguments);
25775         this.sync();
25776     },
25777
25778     setLeftTop : function(left, top){
25779         this.storeLeftTop(left, top);
25780         supr.setLeftTop.apply(this, arguments);
25781         this.sync();
25782     },
25783
25784     setXY : function(xy, a, d, c, e){
25785         this.fixDisplay();
25786         this.beforeAction();
25787         this.storeXY(xy);
25788         var cb = this.createCB(c);
25789         supr.setXY.call(this, xy, a, d, cb, e);
25790         if(!a){
25791             cb();
25792         }
25793     },
25794
25795     // private
25796     createCB : function(c){
25797         var el = this;
25798         return function(){
25799             el.constrainXY();
25800             el.sync(true);
25801             if(c){
25802                 c();
25803             }
25804         };
25805     },
25806
25807     // overridden Element method
25808     setX : function(x, a, d, c, e){
25809         this.setXY([x, this.getY()], a, d, c, e);
25810     },
25811
25812     // overridden Element method
25813     setY : function(y, a, d, c, e){
25814         this.setXY([this.getX(), y], a, d, c, e);
25815     },
25816
25817     // overridden Element method
25818     setSize : function(w, h, a, d, c, e){
25819         this.beforeAction();
25820         var cb = this.createCB(c);
25821         supr.setSize.call(this, w, h, a, d, cb, e);
25822         if(!a){
25823             cb();
25824         }
25825     },
25826
25827     // overridden Element method
25828     setWidth : function(w, a, d, c, e){
25829         this.beforeAction();
25830         var cb = this.createCB(c);
25831         supr.setWidth.call(this, w, a, d, cb, e);
25832         if(!a){
25833             cb();
25834         }
25835     },
25836
25837     // overridden Element method
25838     setHeight : function(h, a, d, c, e){
25839         this.beforeAction();
25840         var cb = this.createCB(c);
25841         supr.setHeight.call(this, h, a, d, cb, e);
25842         if(!a){
25843             cb();
25844         }
25845     },
25846
25847     // overridden Element method
25848     setBounds : function(x, y, w, h, a, d, c, e){
25849         this.beforeAction();
25850         var cb = this.createCB(c);
25851         if(!a){
25852             this.storeXY([x, y]);
25853             supr.setXY.call(this, [x, y]);
25854             supr.setSize.call(this, w, h, a, d, cb, e);
25855             cb();
25856         }else{
25857             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25858         }
25859         return this;
25860     },
25861     
25862     /**
25863      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25864      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25865      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25866      * @param {Number} zindex The new z-index to set
25867      * @return {this} The Layer
25868      */
25869     setZIndex : function(zindex){
25870         this.zindex = zindex;
25871         this.setStyle("z-index", zindex + 2);
25872         if(this.shadow){
25873             this.shadow.setZIndex(zindex + 1);
25874         }
25875         if(this.shim){
25876             this.shim.setStyle("z-index", zindex);
25877         }
25878     }
25879 });
25880 })();/*
25881  * Based on:
25882  * Ext JS Library 1.1.1
25883  * Copyright(c) 2006-2007, Ext JS, LLC.
25884  *
25885  * Originally Released Under LGPL - original licence link has changed is not relivant.
25886  *
25887  * Fork - LGPL
25888  * <script type="text/javascript">
25889  */
25890
25891
25892 /**
25893  * @class Roo.Shadow
25894  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25895  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25896  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25897  * @constructor
25898  * Create a new Shadow
25899  * @param {Object} config The config object
25900  */
25901 Roo.Shadow = function(config){
25902     Roo.apply(this, config);
25903     if(typeof this.mode != "string"){
25904         this.mode = this.defaultMode;
25905     }
25906     var o = this.offset, a = {h: 0};
25907     var rad = Math.floor(this.offset/2);
25908     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25909         case "drop":
25910             a.w = 0;
25911             a.l = a.t = o;
25912             a.t -= 1;
25913             if(Roo.isIE){
25914                 a.l -= this.offset + rad;
25915                 a.t -= this.offset + rad;
25916                 a.w -= rad;
25917                 a.h -= rad;
25918                 a.t += 1;
25919             }
25920         break;
25921         case "sides":
25922             a.w = (o*2);
25923             a.l = -o;
25924             a.t = o-1;
25925             if(Roo.isIE){
25926                 a.l -= (this.offset - rad);
25927                 a.t -= this.offset + rad;
25928                 a.l += 1;
25929                 a.w -= (this.offset - rad)*2;
25930                 a.w -= rad + 1;
25931                 a.h -= 1;
25932             }
25933         break;
25934         case "frame":
25935             a.w = a.h = (o*2);
25936             a.l = a.t = -o;
25937             a.t += 1;
25938             a.h -= 2;
25939             if(Roo.isIE){
25940                 a.l -= (this.offset - rad);
25941                 a.t -= (this.offset - rad);
25942                 a.l += 1;
25943                 a.w -= (this.offset + rad + 1);
25944                 a.h -= (this.offset + rad);
25945                 a.h += 1;
25946             }
25947         break;
25948     };
25949
25950     this.adjusts = a;
25951 };
25952
25953 Roo.Shadow.prototype = {
25954     /**
25955      * @cfg {String} mode
25956      * The shadow display mode.  Supports the following options:<br />
25957      * sides: Shadow displays on both sides and bottom only<br />
25958      * frame: Shadow displays equally on all four sides<br />
25959      * drop: Traditional bottom-right drop shadow (default)
25960      */
25961     /**
25962      * @cfg {String} offset
25963      * The number of pixels to offset the shadow from the element (defaults to 4)
25964      */
25965     offset: 4,
25966
25967     // private
25968     defaultMode: "drop",
25969
25970     /**
25971      * Displays the shadow under the target element
25972      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25973      */
25974     show : function(target){
25975         target = Roo.get(target);
25976         if(!this.el){
25977             this.el = Roo.Shadow.Pool.pull();
25978             if(this.el.dom.nextSibling != target.dom){
25979                 this.el.insertBefore(target);
25980             }
25981         }
25982         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25983         if(Roo.isIE){
25984             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25985         }
25986         this.realign(
25987             target.getLeft(true),
25988             target.getTop(true),
25989             target.getWidth(),
25990             target.getHeight()
25991         );
25992         this.el.dom.style.display = "block";
25993     },
25994
25995     /**
25996      * Returns true if the shadow is visible, else false
25997      */
25998     isVisible : function(){
25999         return this.el ? true : false;  
26000     },
26001
26002     /**
26003      * Direct alignment when values are already available. Show must be called at least once before
26004      * calling this method to ensure it is initialized.
26005      * @param {Number} left The target element left position
26006      * @param {Number} top The target element top position
26007      * @param {Number} width The target element width
26008      * @param {Number} height The target element height
26009      */
26010     realign : function(l, t, w, h){
26011         if(!this.el){
26012             return;
26013         }
26014         var a = this.adjusts, d = this.el.dom, s = d.style;
26015         var iea = 0;
26016         s.left = (l+a.l)+"px";
26017         s.top = (t+a.t)+"px";
26018         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26019  
26020         if(s.width != sws || s.height != shs){
26021             s.width = sws;
26022             s.height = shs;
26023             if(!Roo.isIE){
26024                 var cn = d.childNodes;
26025                 var sww = Math.max(0, (sw-12))+"px";
26026                 cn[0].childNodes[1].style.width = sww;
26027                 cn[1].childNodes[1].style.width = sww;
26028                 cn[2].childNodes[1].style.width = sww;
26029                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26030             }
26031         }
26032     },
26033
26034     /**
26035      * Hides this shadow
26036      */
26037     hide : function(){
26038         if(this.el){
26039             this.el.dom.style.display = "none";
26040             Roo.Shadow.Pool.push(this.el);
26041             delete this.el;
26042         }
26043     },
26044
26045     /**
26046      * Adjust the z-index of this shadow
26047      * @param {Number} zindex The new z-index
26048      */
26049     setZIndex : function(z){
26050         this.zIndex = z;
26051         if(this.el){
26052             this.el.setStyle("z-index", z);
26053         }
26054     }
26055 };
26056
26057 // Private utility class that manages the internal Shadow cache
26058 Roo.Shadow.Pool = function(){
26059     var p = [];
26060     var markup = Roo.isIE ?
26061                  '<div class="x-ie-shadow"></div>' :
26062                  '<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>';
26063     return {
26064         pull : function(){
26065             var sh = p.shift();
26066             if(!sh){
26067                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26068                 sh.autoBoxAdjust = false;
26069             }
26070             return sh;
26071         },
26072
26073         push : function(sh){
26074             p.push(sh);
26075         }
26076     };
26077 }();/*
26078  * Based on:
26079  * Ext JS Library 1.1.1
26080  * Copyright(c) 2006-2007, Ext JS, LLC.
26081  *
26082  * Originally Released Under LGPL - original licence link has changed is not relivant.
26083  *
26084  * Fork - LGPL
26085  * <script type="text/javascript">
26086  */
26087
26088
26089 /**
26090  * @class Roo.SplitBar
26091  * @extends Roo.util.Observable
26092  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26093  * <br><br>
26094  * Usage:
26095  * <pre><code>
26096 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26097                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26098 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26099 split.minSize = 100;
26100 split.maxSize = 600;
26101 split.animate = true;
26102 split.on('moved', splitterMoved);
26103 </code></pre>
26104  * @constructor
26105  * Create a new SplitBar
26106  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26107  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26108  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26109  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26110                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26111                         position of the SplitBar).
26112  */
26113 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26114     
26115     /** @private */
26116     this.el = Roo.get(dragElement, true);
26117     this.el.dom.unselectable = "on";
26118     /** @private */
26119     this.resizingEl = Roo.get(resizingElement, true);
26120
26121     /**
26122      * @private
26123      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26124      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26125      * @type Number
26126      */
26127     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26128     
26129     /**
26130      * The minimum size of the resizing element. (Defaults to 0)
26131      * @type Number
26132      */
26133     this.minSize = 0;
26134     
26135     /**
26136      * The maximum size of the resizing element. (Defaults to 2000)
26137      * @type Number
26138      */
26139     this.maxSize = 2000;
26140     
26141     /**
26142      * Whether to animate the transition to the new size
26143      * @type Boolean
26144      */
26145     this.animate = false;
26146     
26147     /**
26148      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26149      * @type Boolean
26150      */
26151     this.useShim = false;
26152     
26153     /** @private */
26154     this.shim = null;
26155     
26156     if(!existingProxy){
26157         /** @private */
26158         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26159     }else{
26160         this.proxy = Roo.get(existingProxy).dom;
26161     }
26162     /** @private */
26163     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26164     
26165     /** @private */
26166     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26167     
26168     /** @private */
26169     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26170     
26171     /** @private */
26172     this.dragSpecs = {};
26173     
26174     /**
26175      * @private The adapter to use to positon and resize elements
26176      */
26177     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26178     this.adapter.init(this);
26179     
26180     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26181         /** @private */
26182         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26183         this.el.addClass("x-splitbar-h");
26184     }else{
26185         /** @private */
26186         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26187         this.el.addClass("x-splitbar-v");
26188     }
26189     
26190     this.addEvents({
26191         /**
26192          * @event resize
26193          * Fires when the splitter is moved (alias for {@link #event-moved})
26194          * @param {Roo.SplitBar} this
26195          * @param {Number} newSize the new width or height
26196          */
26197         "resize" : true,
26198         /**
26199          * @event moved
26200          * Fires when the splitter is moved
26201          * @param {Roo.SplitBar} this
26202          * @param {Number} newSize the new width or height
26203          */
26204         "moved" : true,
26205         /**
26206          * @event beforeresize
26207          * Fires before the splitter is dragged
26208          * @param {Roo.SplitBar} this
26209          */
26210         "beforeresize" : true,
26211
26212         "beforeapply" : true
26213     });
26214
26215     Roo.util.Observable.call(this);
26216 };
26217
26218 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26219     onStartProxyDrag : function(x, y){
26220         this.fireEvent("beforeresize", this);
26221         if(!this.overlay){
26222             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26223             o.unselectable();
26224             o.enableDisplayMode("block");
26225             // all splitbars share the same overlay
26226             Roo.SplitBar.prototype.overlay = o;
26227         }
26228         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26229         this.overlay.show();
26230         Roo.get(this.proxy).setDisplayed("block");
26231         var size = this.adapter.getElementSize(this);
26232         this.activeMinSize = this.getMinimumSize();;
26233         this.activeMaxSize = this.getMaximumSize();;
26234         var c1 = size - this.activeMinSize;
26235         var c2 = Math.max(this.activeMaxSize - size, 0);
26236         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26237             this.dd.resetConstraints();
26238             this.dd.setXConstraint(
26239                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26240                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26241             );
26242             this.dd.setYConstraint(0, 0);
26243         }else{
26244             this.dd.resetConstraints();
26245             this.dd.setXConstraint(0, 0);
26246             this.dd.setYConstraint(
26247                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26248                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26249             );
26250          }
26251         this.dragSpecs.startSize = size;
26252         this.dragSpecs.startPoint = [x, y];
26253         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26254     },
26255     
26256     /** 
26257      * @private Called after the drag operation by the DDProxy
26258      */
26259     onEndProxyDrag : function(e){
26260         Roo.get(this.proxy).setDisplayed(false);
26261         var endPoint = Roo.lib.Event.getXY(e);
26262         if(this.overlay){
26263             this.overlay.hide();
26264         }
26265         var newSize;
26266         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26267             newSize = this.dragSpecs.startSize + 
26268                 (this.placement == Roo.SplitBar.LEFT ?
26269                     endPoint[0] - this.dragSpecs.startPoint[0] :
26270                     this.dragSpecs.startPoint[0] - endPoint[0]
26271                 );
26272         }else{
26273             newSize = this.dragSpecs.startSize + 
26274                 (this.placement == Roo.SplitBar.TOP ?
26275                     endPoint[1] - this.dragSpecs.startPoint[1] :
26276                     this.dragSpecs.startPoint[1] - endPoint[1]
26277                 );
26278         }
26279         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26280         if(newSize != this.dragSpecs.startSize){
26281             if(this.fireEvent('beforeapply', this, newSize) !== false){
26282                 this.adapter.setElementSize(this, newSize);
26283                 this.fireEvent("moved", this, newSize);
26284                 this.fireEvent("resize", this, newSize);
26285             }
26286         }
26287     },
26288     
26289     /**
26290      * Get the adapter this SplitBar uses
26291      * @return The adapter object
26292      */
26293     getAdapter : function(){
26294         return this.adapter;
26295     },
26296     
26297     /**
26298      * Set the adapter this SplitBar uses
26299      * @param {Object} adapter A SplitBar adapter object
26300      */
26301     setAdapter : function(adapter){
26302         this.adapter = adapter;
26303         this.adapter.init(this);
26304     },
26305     
26306     /**
26307      * Gets the minimum size for the resizing element
26308      * @return {Number} The minimum size
26309      */
26310     getMinimumSize : function(){
26311         return this.minSize;
26312     },
26313     
26314     /**
26315      * Sets the minimum size for the resizing element
26316      * @param {Number} minSize The minimum size
26317      */
26318     setMinimumSize : function(minSize){
26319         this.minSize = minSize;
26320     },
26321     
26322     /**
26323      * Gets the maximum size for the resizing element
26324      * @return {Number} The maximum size
26325      */
26326     getMaximumSize : function(){
26327         return this.maxSize;
26328     },
26329     
26330     /**
26331      * Sets the maximum size for the resizing element
26332      * @param {Number} maxSize The maximum size
26333      */
26334     setMaximumSize : function(maxSize){
26335         this.maxSize = maxSize;
26336     },
26337     
26338     /**
26339      * Sets the initialize size for the resizing element
26340      * @param {Number} size The initial size
26341      */
26342     setCurrentSize : function(size){
26343         var oldAnimate = this.animate;
26344         this.animate = false;
26345         this.adapter.setElementSize(this, size);
26346         this.animate = oldAnimate;
26347     },
26348     
26349     /**
26350      * Destroy this splitbar. 
26351      * @param {Boolean} removeEl True to remove the element
26352      */
26353     destroy : function(removeEl){
26354         if(this.shim){
26355             this.shim.remove();
26356         }
26357         this.dd.unreg();
26358         this.proxy.parentNode.removeChild(this.proxy);
26359         if(removeEl){
26360             this.el.remove();
26361         }
26362     }
26363 });
26364
26365 /**
26366  * @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.
26367  */
26368 Roo.SplitBar.createProxy = function(dir){
26369     var proxy = new Roo.Element(document.createElement("div"));
26370     proxy.unselectable();
26371     var cls = 'x-splitbar-proxy';
26372     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26373     document.body.appendChild(proxy.dom);
26374     return proxy.dom;
26375 };
26376
26377 /** 
26378  * @class Roo.SplitBar.BasicLayoutAdapter
26379  * Default Adapter. It assumes the splitter and resizing element are not positioned
26380  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26381  */
26382 Roo.SplitBar.BasicLayoutAdapter = function(){
26383 };
26384
26385 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26386     // do nothing for now
26387     init : function(s){
26388     
26389     },
26390     /**
26391      * Called before drag operations to get the current size of the resizing element. 
26392      * @param {Roo.SplitBar} s The SplitBar using this adapter
26393      */
26394      getElementSize : function(s){
26395         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26396             return s.resizingEl.getWidth();
26397         }else{
26398             return s.resizingEl.getHeight();
26399         }
26400     },
26401     
26402     /**
26403      * Called after drag operations to set the size of the resizing element.
26404      * @param {Roo.SplitBar} s The SplitBar using this adapter
26405      * @param {Number} newSize The new size to set
26406      * @param {Function} onComplete A function to be invoked when resizing is complete
26407      */
26408     setElementSize : function(s, newSize, onComplete){
26409         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26410             if(!s.animate){
26411                 s.resizingEl.setWidth(newSize);
26412                 if(onComplete){
26413                     onComplete(s, newSize);
26414                 }
26415             }else{
26416                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26417             }
26418         }else{
26419             
26420             if(!s.animate){
26421                 s.resizingEl.setHeight(newSize);
26422                 if(onComplete){
26423                     onComplete(s, newSize);
26424                 }
26425             }else{
26426                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26427             }
26428         }
26429     }
26430 };
26431
26432 /** 
26433  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26434  * @extends Roo.SplitBar.BasicLayoutAdapter
26435  * Adapter that  moves the splitter element to align with the resized sizing element. 
26436  * Used with an absolute positioned SplitBar.
26437  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26438  * document.body, make sure you assign an id to the body element.
26439  */
26440 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26441     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26442     this.container = Roo.get(container);
26443 };
26444
26445 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26446     init : function(s){
26447         this.basic.init(s);
26448     },
26449     
26450     getElementSize : function(s){
26451         return this.basic.getElementSize(s);
26452     },
26453     
26454     setElementSize : function(s, newSize, onComplete){
26455         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26456     },
26457     
26458     moveSplitter : function(s){
26459         var yes = Roo.SplitBar;
26460         switch(s.placement){
26461             case yes.LEFT:
26462                 s.el.setX(s.resizingEl.getRight());
26463                 break;
26464             case yes.RIGHT:
26465                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26466                 break;
26467             case yes.TOP:
26468                 s.el.setY(s.resizingEl.getBottom());
26469                 break;
26470             case yes.BOTTOM:
26471                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26472                 break;
26473         }
26474     }
26475 };
26476
26477 /**
26478  * Orientation constant - Create a vertical SplitBar
26479  * @static
26480  * @type Number
26481  */
26482 Roo.SplitBar.VERTICAL = 1;
26483
26484 /**
26485  * Orientation constant - Create a horizontal SplitBar
26486  * @static
26487  * @type Number
26488  */
26489 Roo.SplitBar.HORIZONTAL = 2;
26490
26491 /**
26492  * Placement constant - The resizing element is to the left of the splitter element
26493  * @static
26494  * @type Number
26495  */
26496 Roo.SplitBar.LEFT = 1;
26497
26498 /**
26499  * Placement constant - The resizing element is to the right of the splitter element
26500  * @static
26501  * @type Number
26502  */
26503 Roo.SplitBar.RIGHT = 2;
26504
26505 /**
26506  * Placement constant - The resizing element is positioned above the splitter element
26507  * @static
26508  * @type Number
26509  */
26510 Roo.SplitBar.TOP = 3;
26511
26512 /**
26513  * Placement constant - The resizing element is positioned under splitter element
26514  * @static
26515  * @type Number
26516  */
26517 Roo.SplitBar.BOTTOM = 4;
26518 /*
26519  * Based on:
26520  * Ext JS Library 1.1.1
26521  * Copyright(c) 2006-2007, Ext JS, LLC.
26522  *
26523  * Originally Released Under LGPL - original licence link has changed is not relivant.
26524  *
26525  * Fork - LGPL
26526  * <script type="text/javascript">
26527  */
26528
26529 /**
26530  * @class Roo.View
26531  * @extends Roo.util.Observable
26532  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26533  * This class also supports single and multi selection modes. <br>
26534  * Create a data model bound view:
26535  <pre><code>
26536  var store = new Roo.data.Store(...);
26537
26538  var view = new Roo.View({
26539     el : "my-element",
26540     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26541  
26542     singleSelect: true,
26543     selectedClass: "ydataview-selected",
26544     store: store
26545  });
26546
26547  // listen for node click?
26548  view.on("click", function(vw, index, node, e){
26549  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26550  });
26551
26552  // load XML data
26553  dataModel.load("foobar.xml");
26554  </code></pre>
26555  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26556  * <br><br>
26557  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26558  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26559  * 
26560  * Note: old style constructor is still suported (container, template, config)
26561  * 
26562  * @constructor
26563  * Create a new View
26564  * @param {Object} config The config object
26565  * 
26566  */
26567 Roo.View = function(config, depreciated_tpl, depreciated_config){
26568     
26569     this.parent = false;
26570     
26571     if (typeof(depreciated_tpl) == 'undefined') {
26572         // new way.. - universal constructor.
26573         Roo.apply(this, config);
26574         this.el  = Roo.get(this.el);
26575     } else {
26576         // old format..
26577         this.el  = Roo.get(config);
26578         this.tpl = depreciated_tpl;
26579         Roo.apply(this, depreciated_config);
26580     }
26581     this.wrapEl  = this.el.wrap().wrap();
26582     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26583     
26584     
26585     if(typeof(this.tpl) == "string"){
26586         this.tpl = new Roo.Template(this.tpl);
26587     } else {
26588         // support xtype ctors..
26589         this.tpl = new Roo.factory(this.tpl, Roo);
26590     }
26591     
26592     
26593     this.tpl.compile();
26594     
26595     /** @private */
26596     this.addEvents({
26597         /**
26598          * @event beforeclick
26599          * Fires before a click is processed. Returns false to cancel the default action.
26600          * @param {Roo.View} this
26601          * @param {Number} index The index of the target node
26602          * @param {HTMLElement} node The target node
26603          * @param {Roo.EventObject} e The raw event object
26604          */
26605             "beforeclick" : true,
26606         /**
26607          * @event click
26608          * Fires when a template node is clicked.
26609          * @param {Roo.View} this
26610          * @param {Number} index The index of the target node
26611          * @param {HTMLElement} node The target node
26612          * @param {Roo.EventObject} e The raw event object
26613          */
26614             "click" : true,
26615         /**
26616          * @event dblclick
26617          * Fires when a template node is double clicked.
26618          * @param {Roo.View} this
26619          * @param {Number} index The index of the target node
26620          * @param {HTMLElement} node The target node
26621          * @param {Roo.EventObject} e The raw event object
26622          */
26623             "dblclick" : true,
26624         /**
26625          * @event contextmenu
26626          * Fires when a template node is right clicked.
26627          * @param {Roo.View} this
26628          * @param {Number} index The index of the target node
26629          * @param {HTMLElement} node The target node
26630          * @param {Roo.EventObject} e The raw event object
26631          */
26632             "contextmenu" : true,
26633         /**
26634          * @event selectionchange
26635          * Fires when the selected nodes change.
26636          * @param {Roo.View} this
26637          * @param {Array} selections Array of the selected nodes
26638          */
26639             "selectionchange" : true,
26640     
26641         /**
26642          * @event beforeselect
26643          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26644          * @param {Roo.View} this
26645          * @param {HTMLElement} node The node to be selected
26646          * @param {Array} selections Array of currently selected nodes
26647          */
26648             "beforeselect" : true,
26649         /**
26650          * @event preparedata
26651          * Fires on every row to render, to allow you to change the data.
26652          * @param {Roo.View} this
26653          * @param {Object} data to be rendered (change this)
26654          */
26655           "preparedata" : true
26656           
26657           
26658         });
26659
26660
26661
26662     this.el.on({
26663         "click": this.onClick,
26664         "dblclick": this.onDblClick,
26665         "contextmenu": this.onContextMenu,
26666         scope:this
26667     });
26668
26669     this.selections = [];
26670     this.nodes = [];
26671     this.cmp = new Roo.CompositeElementLite([]);
26672     if(this.store){
26673         this.store = Roo.factory(this.store, Roo.data);
26674         this.setStore(this.store, true);
26675     }
26676     
26677     if ( this.footer && this.footer.xtype) {
26678            
26679          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26680         
26681         this.footer.dataSource = this.store;
26682         this.footer.container = fctr;
26683         this.footer = Roo.factory(this.footer, Roo);
26684         fctr.insertFirst(this.el);
26685         
26686         // this is a bit insane - as the paging toolbar seems to detach the el..
26687 //        dom.parentNode.parentNode.parentNode
26688          // they get detached?
26689     }
26690     
26691     
26692     Roo.View.superclass.constructor.call(this);
26693     
26694     
26695 };
26696
26697 Roo.extend(Roo.View, Roo.util.Observable, {
26698     
26699      /**
26700      * @cfg {Roo.data.Store} store Data store to load data from.
26701      */
26702     store : false,
26703     
26704     /**
26705      * @cfg {String|Roo.Element} el The container element.
26706      */
26707     el : '',
26708     
26709     /**
26710      * @cfg {String|Roo.Template} tpl The template used by this View 
26711      */
26712     tpl : false,
26713     /**
26714      * @cfg {String} dataName the named area of the template to use as the data area
26715      *                          Works with domtemplates roo-name="name"
26716      */
26717     dataName: false,
26718     /**
26719      * @cfg {String} selectedClass The css class to add to selected nodes
26720      */
26721     selectedClass : "x-view-selected",
26722      /**
26723      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26724      */
26725     emptyText : "",
26726     
26727     /**
26728      * @cfg {String} text to display on mask (default Loading)
26729      */
26730     mask : false,
26731     /**
26732      * @cfg {Boolean} multiSelect Allow multiple selection
26733      */
26734     multiSelect : false,
26735     /**
26736      * @cfg {Boolean} singleSelect Allow single selection
26737      */
26738     singleSelect:  false,
26739     
26740     /**
26741      * @cfg {Boolean} toggleSelect - selecting 
26742      */
26743     toggleSelect : false,
26744     
26745     /**
26746      * @cfg {Boolean} tickable - selecting 
26747      */
26748     tickable : false,
26749     
26750     /**
26751      * Returns the element this view is bound to.
26752      * @return {Roo.Element}
26753      */
26754     getEl : function(){
26755         return this.wrapEl;
26756     },
26757     
26758     
26759
26760     /**
26761      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26762      */
26763     refresh : function(){
26764         //Roo.log('refresh');
26765         var t = this.tpl;
26766         
26767         // if we are using something like 'domtemplate', then
26768         // the what gets used is:
26769         // t.applySubtemplate(NAME, data, wrapping data..)
26770         // the outer template then get' applied with
26771         //     the store 'extra data'
26772         // and the body get's added to the
26773         //      roo-name="data" node?
26774         //      <span class='roo-tpl-{name}'></span> ?????
26775         
26776         
26777         
26778         this.clearSelections();
26779         this.el.update("");
26780         var html = [];
26781         var records = this.store.getRange();
26782         if(records.length < 1) {
26783             
26784             // is this valid??  = should it render a template??
26785             
26786             this.el.update(this.emptyText);
26787             return;
26788         }
26789         var el = this.el;
26790         if (this.dataName) {
26791             this.el.update(t.apply(this.store.meta)); //????
26792             el = this.el.child('.roo-tpl-' + this.dataName);
26793         }
26794         
26795         for(var i = 0, len = records.length; i < len; i++){
26796             var data = this.prepareData(records[i].data, i, records[i]);
26797             this.fireEvent("preparedata", this, data, i, records[i]);
26798             
26799             var d = Roo.apply({}, data);
26800             
26801             if(this.tickable){
26802                 Roo.apply(d, {'roo-id' : Roo.id()});
26803                 
26804                 var _this = this;
26805             
26806                 Roo.each(this.parent.item, function(item){
26807                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26808                         return;
26809                     }
26810                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26811                 });
26812             }
26813             
26814             html[html.length] = Roo.util.Format.trim(
26815                 this.dataName ?
26816                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26817                     t.apply(d)
26818             );
26819         }
26820         
26821         
26822         
26823         el.update(html.join(""));
26824         this.nodes = el.dom.childNodes;
26825         this.updateIndexes(0);
26826     },
26827     
26828
26829     /**
26830      * Function to override to reformat the data that is sent to
26831      * the template for each node.
26832      * DEPRICATED - use the preparedata event handler.
26833      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26834      * a JSON object for an UpdateManager bound view).
26835      */
26836     prepareData : function(data, index, record)
26837     {
26838         this.fireEvent("preparedata", this, data, index, record);
26839         return data;
26840     },
26841
26842     onUpdate : function(ds, record){
26843         // Roo.log('on update');   
26844         this.clearSelections();
26845         var index = this.store.indexOf(record);
26846         var n = this.nodes[index];
26847         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26848         n.parentNode.removeChild(n);
26849         this.updateIndexes(index, index);
26850     },
26851
26852     
26853     
26854 // --------- FIXME     
26855     onAdd : function(ds, records, index)
26856     {
26857         //Roo.log(['on Add', ds, records, index] );        
26858         this.clearSelections();
26859         if(this.nodes.length == 0){
26860             this.refresh();
26861             return;
26862         }
26863         var n = this.nodes[index];
26864         for(var i = 0, len = records.length; i < len; i++){
26865             var d = this.prepareData(records[i].data, i, records[i]);
26866             if(n){
26867                 this.tpl.insertBefore(n, d);
26868             }else{
26869                 
26870                 this.tpl.append(this.el, d);
26871             }
26872         }
26873         this.updateIndexes(index);
26874     },
26875
26876     onRemove : function(ds, record, index){
26877        // Roo.log('onRemove');
26878         this.clearSelections();
26879         var el = this.dataName  ?
26880             this.el.child('.roo-tpl-' + this.dataName) :
26881             this.el; 
26882         
26883         el.dom.removeChild(this.nodes[index]);
26884         this.updateIndexes(index);
26885     },
26886
26887     /**
26888      * Refresh an individual node.
26889      * @param {Number} index
26890      */
26891     refreshNode : function(index){
26892         this.onUpdate(this.store, this.store.getAt(index));
26893     },
26894
26895     updateIndexes : function(startIndex, endIndex){
26896         var ns = this.nodes;
26897         startIndex = startIndex || 0;
26898         endIndex = endIndex || ns.length - 1;
26899         for(var i = startIndex; i <= endIndex; i++){
26900             ns[i].nodeIndex = i;
26901         }
26902     },
26903
26904     /**
26905      * Changes the data store this view uses and refresh the view.
26906      * @param {Store} store
26907      */
26908     setStore : function(store, initial){
26909         if(!initial && this.store){
26910             this.store.un("datachanged", this.refresh);
26911             this.store.un("add", this.onAdd);
26912             this.store.un("remove", this.onRemove);
26913             this.store.un("update", this.onUpdate);
26914             this.store.un("clear", this.refresh);
26915             this.store.un("beforeload", this.onBeforeLoad);
26916             this.store.un("load", this.onLoad);
26917             this.store.un("loadexception", this.onLoad);
26918         }
26919         if(store){
26920           
26921             store.on("datachanged", this.refresh, this);
26922             store.on("add", this.onAdd, this);
26923             store.on("remove", this.onRemove, this);
26924             store.on("update", this.onUpdate, this);
26925             store.on("clear", this.refresh, this);
26926             store.on("beforeload", this.onBeforeLoad, this);
26927             store.on("load", this.onLoad, this);
26928             store.on("loadexception", this.onLoad, this);
26929         }
26930         
26931         if(store){
26932             this.refresh();
26933         }
26934     },
26935     /**
26936      * onbeforeLoad - masks the loading area.
26937      *
26938      */
26939     onBeforeLoad : function(store,opts)
26940     {
26941          //Roo.log('onBeforeLoad');   
26942         if (!opts.add) {
26943             this.el.update("");
26944         }
26945         this.el.mask(this.mask ? this.mask : "Loading" ); 
26946     },
26947     onLoad : function ()
26948     {
26949         this.el.unmask();
26950     },
26951     
26952
26953     /**
26954      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26955      * @param {HTMLElement} node
26956      * @return {HTMLElement} The template node
26957      */
26958     findItemFromChild : function(node){
26959         var el = this.dataName  ?
26960             this.el.child('.roo-tpl-' + this.dataName,true) :
26961             this.el.dom; 
26962         
26963         if(!node || node.parentNode == el){
26964                     return node;
26965             }
26966             var p = node.parentNode;
26967             while(p && p != el){
26968             if(p.parentNode == el){
26969                 return p;
26970             }
26971             p = p.parentNode;
26972         }
26973             return null;
26974     },
26975
26976     /** @ignore */
26977     onClick : function(e){
26978         var item = this.findItemFromChild(e.getTarget());
26979         if(item){
26980             var index = this.indexOf(item);
26981             if(this.onItemClick(item, index, e) !== false){
26982                 this.fireEvent("click", this, index, item, e);
26983             }
26984         }else{
26985             this.clearSelections();
26986         }
26987     },
26988
26989     /** @ignore */
26990     onContextMenu : function(e){
26991         var item = this.findItemFromChild(e.getTarget());
26992         if(item){
26993             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26994         }
26995     },
26996
26997     /** @ignore */
26998     onDblClick : function(e){
26999         var item = this.findItemFromChild(e.getTarget());
27000         if(item){
27001             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27002         }
27003     },
27004
27005     onItemClick : function(item, index, e)
27006     {
27007         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27008             return false;
27009         }
27010         if (this.toggleSelect) {
27011             var m = this.isSelected(item) ? 'unselect' : 'select';
27012             //Roo.log(m);
27013             var _t = this;
27014             _t[m](item, true, false);
27015             return true;
27016         }
27017         if(this.multiSelect || this.singleSelect){
27018             if(this.multiSelect && e.shiftKey && this.lastSelection){
27019                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27020             }else{
27021                 this.select(item, this.multiSelect && e.ctrlKey);
27022                 this.lastSelection = item;
27023             }
27024             
27025             if(!this.tickable){
27026                 e.preventDefault();
27027             }
27028             
27029         }
27030         return true;
27031     },
27032
27033     /**
27034      * Get the number of selected nodes.
27035      * @return {Number}
27036      */
27037     getSelectionCount : function(){
27038         return this.selections.length;
27039     },
27040
27041     /**
27042      * Get the currently selected nodes.
27043      * @return {Array} An array of HTMLElements
27044      */
27045     getSelectedNodes : function(){
27046         return this.selections;
27047     },
27048
27049     /**
27050      * Get the indexes of the selected nodes.
27051      * @return {Array}
27052      */
27053     getSelectedIndexes : function(){
27054         var indexes = [], s = this.selections;
27055         for(var i = 0, len = s.length; i < len; i++){
27056             indexes.push(s[i].nodeIndex);
27057         }
27058         return indexes;
27059     },
27060
27061     /**
27062      * Clear all selections
27063      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27064      */
27065     clearSelections : function(suppressEvent){
27066         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27067             this.cmp.elements = this.selections;
27068             this.cmp.removeClass(this.selectedClass);
27069             this.selections = [];
27070             if(!suppressEvent){
27071                 this.fireEvent("selectionchange", this, this.selections);
27072             }
27073         }
27074     },
27075
27076     /**
27077      * Returns true if the passed node is selected
27078      * @param {HTMLElement/Number} node The node or node index
27079      * @return {Boolean}
27080      */
27081     isSelected : function(node){
27082         var s = this.selections;
27083         if(s.length < 1){
27084             return false;
27085         }
27086         node = this.getNode(node);
27087         return s.indexOf(node) !== -1;
27088     },
27089
27090     /**
27091      * Selects nodes.
27092      * @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
27093      * @param {Boolean} keepExisting (optional) true to keep existing selections
27094      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27095      */
27096     select : function(nodeInfo, keepExisting, suppressEvent){
27097         if(nodeInfo instanceof Array){
27098             if(!keepExisting){
27099                 this.clearSelections(true);
27100             }
27101             for(var i = 0, len = nodeInfo.length; i < len; i++){
27102                 this.select(nodeInfo[i], true, true);
27103             }
27104             return;
27105         } 
27106         var node = this.getNode(nodeInfo);
27107         if(!node || this.isSelected(node)){
27108             return; // already selected.
27109         }
27110         if(!keepExisting){
27111             this.clearSelections(true);
27112         }
27113         
27114         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27115             Roo.fly(node).addClass(this.selectedClass);
27116             this.selections.push(node);
27117             if(!suppressEvent){
27118                 this.fireEvent("selectionchange", this, this.selections);
27119             }
27120         }
27121         
27122         
27123     },
27124       /**
27125      * Unselects nodes.
27126      * @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
27127      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27128      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27129      */
27130     unselect : function(nodeInfo, keepExisting, suppressEvent)
27131     {
27132         if(nodeInfo instanceof Array){
27133             Roo.each(this.selections, function(s) {
27134                 this.unselect(s, nodeInfo);
27135             }, this);
27136             return;
27137         }
27138         var node = this.getNode(nodeInfo);
27139         if(!node || !this.isSelected(node)){
27140             //Roo.log("not selected");
27141             return; // not selected.
27142         }
27143         // fireevent???
27144         var ns = [];
27145         Roo.each(this.selections, function(s) {
27146             if (s == node ) {
27147                 Roo.fly(node).removeClass(this.selectedClass);
27148
27149                 return;
27150             }
27151             ns.push(s);
27152         },this);
27153         
27154         this.selections= ns;
27155         this.fireEvent("selectionchange", this, this.selections);
27156     },
27157
27158     /**
27159      * Gets a template node.
27160      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27161      * @return {HTMLElement} The node or null if it wasn't found
27162      */
27163     getNode : function(nodeInfo){
27164         if(typeof nodeInfo == "string"){
27165             return document.getElementById(nodeInfo);
27166         }else if(typeof nodeInfo == "number"){
27167             return this.nodes[nodeInfo];
27168         }
27169         return nodeInfo;
27170     },
27171
27172     /**
27173      * Gets a range template nodes.
27174      * @param {Number} startIndex
27175      * @param {Number} endIndex
27176      * @return {Array} An array of nodes
27177      */
27178     getNodes : function(start, end){
27179         var ns = this.nodes;
27180         start = start || 0;
27181         end = typeof end == "undefined" ? ns.length - 1 : end;
27182         var nodes = [];
27183         if(start <= end){
27184             for(var i = start; i <= end; i++){
27185                 nodes.push(ns[i]);
27186             }
27187         } else{
27188             for(var i = start; i >= end; i--){
27189                 nodes.push(ns[i]);
27190             }
27191         }
27192         return nodes;
27193     },
27194
27195     /**
27196      * Finds the index of the passed node
27197      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27198      * @return {Number} The index of the node or -1
27199      */
27200     indexOf : function(node){
27201         node = this.getNode(node);
27202         if(typeof node.nodeIndex == "number"){
27203             return node.nodeIndex;
27204         }
27205         var ns = this.nodes;
27206         for(var i = 0, len = ns.length; i < len; i++){
27207             if(ns[i] == node){
27208                 return i;
27209             }
27210         }
27211         return -1;
27212     }
27213 });
27214 /*
27215  * Based on:
27216  * Ext JS Library 1.1.1
27217  * Copyright(c) 2006-2007, Ext JS, LLC.
27218  *
27219  * Originally Released Under LGPL - original licence link has changed is not relivant.
27220  *
27221  * Fork - LGPL
27222  * <script type="text/javascript">
27223  */
27224
27225 /**
27226  * @class Roo.JsonView
27227  * @extends Roo.View
27228  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27229 <pre><code>
27230 var view = new Roo.JsonView({
27231     container: "my-element",
27232     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27233     multiSelect: true, 
27234     jsonRoot: "data" 
27235 });
27236
27237 // listen for node click?
27238 view.on("click", function(vw, index, node, e){
27239     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27240 });
27241
27242 // direct load of JSON data
27243 view.load("foobar.php");
27244
27245 // Example from my blog list
27246 var tpl = new Roo.Template(
27247     '&lt;div class="entry"&gt;' +
27248     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27249     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27250     "&lt;/div&gt;&lt;hr /&gt;"
27251 );
27252
27253 var moreView = new Roo.JsonView({
27254     container :  "entry-list", 
27255     template : tpl,
27256     jsonRoot: "posts"
27257 });
27258 moreView.on("beforerender", this.sortEntries, this);
27259 moreView.load({
27260     url: "/blog/get-posts.php",
27261     params: "allposts=true",
27262     text: "Loading Blog Entries..."
27263 });
27264 </code></pre>
27265
27266 * Note: old code is supported with arguments : (container, template, config)
27267
27268
27269  * @constructor
27270  * Create a new JsonView
27271  * 
27272  * @param {Object} config The config object
27273  * 
27274  */
27275 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27276     
27277     
27278     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27279
27280     var um = this.el.getUpdateManager();
27281     um.setRenderer(this);
27282     um.on("update", this.onLoad, this);
27283     um.on("failure", this.onLoadException, this);
27284
27285     /**
27286      * @event beforerender
27287      * Fires before rendering of the downloaded JSON data.
27288      * @param {Roo.JsonView} this
27289      * @param {Object} data The JSON data loaded
27290      */
27291     /**
27292      * @event load
27293      * Fires when data is loaded.
27294      * @param {Roo.JsonView} this
27295      * @param {Object} data The JSON data loaded
27296      * @param {Object} response The raw Connect response object
27297      */
27298     /**
27299      * @event loadexception
27300      * Fires when loading fails.
27301      * @param {Roo.JsonView} this
27302      * @param {Object} response The raw Connect response object
27303      */
27304     this.addEvents({
27305         'beforerender' : true,
27306         'load' : true,
27307         'loadexception' : true
27308     });
27309 };
27310 Roo.extend(Roo.JsonView, Roo.View, {
27311     /**
27312      * @type {String} The root property in the loaded JSON object that contains the data
27313      */
27314     jsonRoot : "",
27315
27316     /**
27317      * Refreshes the view.
27318      */
27319     refresh : function(){
27320         this.clearSelections();
27321         this.el.update("");
27322         var html = [];
27323         var o = this.jsonData;
27324         if(o && o.length > 0){
27325             for(var i = 0, len = o.length; i < len; i++){
27326                 var data = this.prepareData(o[i], i, o);
27327                 html[html.length] = this.tpl.apply(data);
27328             }
27329         }else{
27330             html.push(this.emptyText);
27331         }
27332         this.el.update(html.join(""));
27333         this.nodes = this.el.dom.childNodes;
27334         this.updateIndexes(0);
27335     },
27336
27337     /**
27338      * 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.
27339      * @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:
27340      <pre><code>
27341      view.load({
27342          url: "your-url.php",
27343          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27344          callback: yourFunction,
27345          scope: yourObject, //(optional scope)
27346          discardUrl: false,
27347          nocache: false,
27348          text: "Loading...",
27349          timeout: 30,
27350          scripts: false
27351      });
27352      </code></pre>
27353      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27354      * 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.
27355      * @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}
27356      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27357      * @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.
27358      */
27359     load : function(){
27360         var um = this.el.getUpdateManager();
27361         um.update.apply(um, arguments);
27362     },
27363
27364     // note - render is a standard framework call...
27365     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27366     render : function(el, response){
27367         
27368         this.clearSelections();
27369         this.el.update("");
27370         var o;
27371         try{
27372             if (response != '') {
27373                 o = Roo.util.JSON.decode(response.responseText);
27374                 if(this.jsonRoot){
27375                     
27376                     o = o[this.jsonRoot];
27377                 }
27378             }
27379         } catch(e){
27380         }
27381         /**
27382          * The current JSON data or null
27383          */
27384         this.jsonData = o;
27385         this.beforeRender();
27386         this.refresh();
27387     },
27388
27389 /**
27390  * Get the number of records in the current JSON dataset
27391  * @return {Number}
27392  */
27393     getCount : function(){
27394         return this.jsonData ? this.jsonData.length : 0;
27395     },
27396
27397 /**
27398  * Returns the JSON object for the specified node(s)
27399  * @param {HTMLElement/Array} node The node or an array of nodes
27400  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27401  * you get the JSON object for the node
27402  */
27403     getNodeData : function(node){
27404         if(node instanceof Array){
27405             var data = [];
27406             for(var i = 0, len = node.length; i < len; i++){
27407                 data.push(this.getNodeData(node[i]));
27408             }
27409             return data;
27410         }
27411         return this.jsonData[this.indexOf(node)] || null;
27412     },
27413
27414     beforeRender : function(){
27415         this.snapshot = this.jsonData;
27416         if(this.sortInfo){
27417             this.sort.apply(this, this.sortInfo);
27418         }
27419         this.fireEvent("beforerender", this, this.jsonData);
27420     },
27421
27422     onLoad : function(el, o){
27423         this.fireEvent("load", this, this.jsonData, o);
27424     },
27425
27426     onLoadException : function(el, o){
27427         this.fireEvent("loadexception", this, o);
27428     },
27429
27430 /**
27431  * Filter the data by a specific property.
27432  * @param {String} property A property on your JSON objects
27433  * @param {String/RegExp} value Either string that the property values
27434  * should start with, or a RegExp to test against the property
27435  */
27436     filter : function(property, value){
27437         if(this.jsonData){
27438             var data = [];
27439             var ss = this.snapshot;
27440             if(typeof value == "string"){
27441                 var vlen = value.length;
27442                 if(vlen == 0){
27443                     this.clearFilter();
27444                     return;
27445                 }
27446                 value = value.toLowerCase();
27447                 for(var i = 0, len = ss.length; i < len; i++){
27448                     var o = ss[i];
27449                     if(o[property].substr(0, vlen).toLowerCase() == value){
27450                         data.push(o);
27451                     }
27452                 }
27453             } else if(value.exec){ // regex?
27454                 for(var i = 0, len = ss.length; i < len; i++){
27455                     var o = ss[i];
27456                     if(value.test(o[property])){
27457                         data.push(o);
27458                     }
27459                 }
27460             } else{
27461                 return;
27462             }
27463             this.jsonData = data;
27464             this.refresh();
27465         }
27466     },
27467
27468 /**
27469  * Filter by a function. The passed function will be called with each
27470  * object in the current dataset. If the function returns true the value is kept,
27471  * otherwise it is filtered.
27472  * @param {Function} fn
27473  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27474  */
27475     filterBy : function(fn, scope){
27476         if(this.jsonData){
27477             var data = [];
27478             var ss = this.snapshot;
27479             for(var i = 0, len = ss.length; i < len; i++){
27480                 var o = ss[i];
27481                 if(fn.call(scope || this, o)){
27482                     data.push(o);
27483                 }
27484             }
27485             this.jsonData = data;
27486             this.refresh();
27487         }
27488     },
27489
27490 /**
27491  * Clears the current filter.
27492  */
27493     clearFilter : function(){
27494         if(this.snapshot && this.jsonData != this.snapshot){
27495             this.jsonData = this.snapshot;
27496             this.refresh();
27497         }
27498     },
27499
27500
27501 /**
27502  * Sorts the data for this view and refreshes it.
27503  * @param {String} property A property on your JSON objects to sort on
27504  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27505  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27506  */
27507     sort : function(property, dir, sortType){
27508         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27509         if(this.jsonData){
27510             var p = property;
27511             var dsc = dir && dir.toLowerCase() == "desc";
27512             var f = function(o1, o2){
27513                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27514                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27515                 ;
27516                 if(v1 < v2){
27517                     return dsc ? +1 : -1;
27518                 } else if(v1 > v2){
27519                     return dsc ? -1 : +1;
27520                 } else{
27521                     return 0;
27522                 }
27523             };
27524             this.jsonData.sort(f);
27525             this.refresh();
27526             if(this.jsonData != this.snapshot){
27527                 this.snapshot.sort(f);
27528             }
27529         }
27530     }
27531 });/*
27532  * Based on:
27533  * Ext JS Library 1.1.1
27534  * Copyright(c) 2006-2007, Ext JS, LLC.
27535  *
27536  * Originally Released Under LGPL - original licence link has changed is not relivant.
27537  *
27538  * Fork - LGPL
27539  * <script type="text/javascript">
27540  */
27541  
27542
27543 /**
27544  * @class Roo.ColorPalette
27545  * @extends Roo.Component
27546  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27547  * Here's an example of typical usage:
27548  * <pre><code>
27549 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27550 cp.render('my-div');
27551
27552 cp.on('select', function(palette, selColor){
27553     // do something with selColor
27554 });
27555 </code></pre>
27556  * @constructor
27557  * Create a new ColorPalette
27558  * @param {Object} config The config object
27559  */
27560 Roo.ColorPalette = function(config){
27561     Roo.ColorPalette.superclass.constructor.call(this, config);
27562     this.addEvents({
27563         /**
27564              * @event select
27565              * Fires when a color is selected
27566              * @param {ColorPalette} this
27567              * @param {String} color The 6-digit color hex code (without the # symbol)
27568              */
27569         select: true
27570     });
27571
27572     if(this.handler){
27573         this.on("select", this.handler, this.scope, true);
27574     }
27575 };
27576 Roo.extend(Roo.ColorPalette, Roo.Component, {
27577     /**
27578      * @cfg {String} itemCls
27579      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27580      */
27581     itemCls : "x-color-palette",
27582     /**
27583      * @cfg {String} value
27584      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27585      * the hex codes are case-sensitive.
27586      */
27587     value : null,
27588     clickEvent:'click',
27589     // private
27590     ctype: "Roo.ColorPalette",
27591
27592     /**
27593      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27594      */
27595     allowReselect : false,
27596
27597     /**
27598      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27599      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27600      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27601      * of colors with the width setting until the box is symmetrical.</p>
27602      * <p>You can override individual colors if needed:</p>
27603      * <pre><code>
27604 var cp = new Roo.ColorPalette();
27605 cp.colors[0] = "FF0000";  // change the first box to red
27606 </code></pre>
27607
27608 Or you can provide a custom array of your own for complete control:
27609 <pre><code>
27610 var cp = new Roo.ColorPalette();
27611 cp.colors = ["000000", "993300", "333300"];
27612 </code></pre>
27613      * @type Array
27614      */
27615     colors : [
27616         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27617         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27618         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27619         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27620         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27621     ],
27622
27623     // private
27624     onRender : function(container, position){
27625         var t = new Roo.MasterTemplate(
27626             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27627         );
27628         var c = this.colors;
27629         for(var i = 0, len = c.length; i < len; i++){
27630             t.add([c[i]]);
27631         }
27632         var el = document.createElement("div");
27633         el.className = this.itemCls;
27634         t.overwrite(el);
27635         container.dom.insertBefore(el, position);
27636         this.el = Roo.get(el);
27637         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27638         if(this.clickEvent != 'click'){
27639             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27640         }
27641     },
27642
27643     // private
27644     afterRender : function(){
27645         Roo.ColorPalette.superclass.afterRender.call(this);
27646         if(this.value){
27647             var s = this.value;
27648             this.value = null;
27649             this.select(s);
27650         }
27651     },
27652
27653     // private
27654     handleClick : function(e, t){
27655         e.preventDefault();
27656         if(!this.disabled){
27657             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27658             this.select(c.toUpperCase());
27659         }
27660     },
27661
27662     /**
27663      * Selects the specified color in the palette (fires the select event)
27664      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27665      */
27666     select : function(color){
27667         color = color.replace("#", "");
27668         if(color != this.value || this.allowReselect){
27669             var el = this.el;
27670             if(this.value){
27671                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27672             }
27673             el.child("a.color-"+color).addClass("x-color-palette-sel");
27674             this.value = color;
27675             this.fireEvent("select", this, color);
27676         }
27677     }
27678 });/*
27679  * Based on:
27680  * Ext JS Library 1.1.1
27681  * Copyright(c) 2006-2007, Ext JS, LLC.
27682  *
27683  * Originally Released Under LGPL - original licence link has changed is not relivant.
27684  *
27685  * Fork - LGPL
27686  * <script type="text/javascript">
27687  */
27688  
27689 /**
27690  * @class Roo.DatePicker
27691  * @extends Roo.Component
27692  * Simple date picker class.
27693  * @constructor
27694  * Create a new DatePicker
27695  * @param {Object} config The config object
27696  */
27697 Roo.DatePicker = function(config){
27698     Roo.DatePicker.superclass.constructor.call(this, config);
27699
27700     this.value = config && config.value ?
27701                  config.value.clearTime() : new Date().clearTime();
27702
27703     this.addEvents({
27704         /**
27705              * @event select
27706              * Fires when a date is selected
27707              * @param {DatePicker} this
27708              * @param {Date} date The selected date
27709              */
27710         'select': true,
27711         /**
27712              * @event monthchange
27713              * Fires when the displayed month changes 
27714              * @param {DatePicker} this
27715              * @param {Date} date The selected month
27716              */
27717         'monthchange': true
27718     });
27719
27720     if(this.handler){
27721         this.on("select", this.handler,  this.scope || this);
27722     }
27723     // build the disabledDatesRE
27724     if(!this.disabledDatesRE && this.disabledDates){
27725         var dd = this.disabledDates;
27726         var re = "(?:";
27727         for(var i = 0; i < dd.length; i++){
27728             re += dd[i];
27729             if(i != dd.length-1) {
27730                 re += "|";
27731             }
27732         }
27733         this.disabledDatesRE = new RegExp(re + ")");
27734     }
27735 };
27736
27737 Roo.extend(Roo.DatePicker, Roo.Component, {
27738     /**
27739      * @cfg {String} todayText
27740      * The text to display on the button that selects the current date (defaults to "Today")
27741      */
27742     todayText : "Today",
27743     /**
27744      * @cfg {String} okText
27745      * The text to display on the ok button
27746      */
27747     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27748     /**
27749      * @cfg {String} cancelText
27750      * The text to display on the cancel button
27751      */
27752     cancelText : "Cancel",
27753     /**
27754      * @cfg {String} todayTip
27755      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27756      */
27757     todayTip : "{0} (Spacebar)",
27758     /**
27759      * @cfg {Date} minDate
27760      * Minimum allowable date (JavaScript date object, defaults to null)
27761      */
27762     minDate : null,
27763     /**
27764      * @cfg {Date} maxDate
27765      * Maximum allowable date (JavaScript date object, defaults to null)
27766      */
27767     maxDate : null,
27768     /**
27769      * @cfg {String} minText
27770      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27771      */
27772     minText : "This date is before the minimum date",
27773     /**
27774      * @cfg {String} maxText
27775      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27776      */
27777     maxText : "This date is after the maximum date",
27778     /**
27779      * @cfg {String} format
27780      * The default date format string which can be overriden for localization support.  The format must be
27781      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27782      */
27783     format : "m/d/y",
27784     /**
27785      * @cfg {Array} disabledDays
27786      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27787      */
27788     disabledDays : null,
27789     /**
27790      * @cfg {String} disabledDaysText
27791      * The tooltip to display when the date falls on a disabled day (defaults to "")
27792      */
27793     disabledDaysText : "",
27794     /**
27795      * @cfg {RegExp} disabledDatesRE
27796      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27797      */
27798     disabledDatesRE : null,
27799     /**
27800      * @cfg {String} disabledDatesText
27801      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27802      */
27803     disabledDatesText : "",
27804     /**
27805      * @cfg {Boolean} constrainToViewport
27806      * True to constrain the date picker to the viewport (defaults to true)
27807      */
27808     constrainToViewport : true,
27809     /**
27810      * @cfg {Array} monthNames
27811      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27812      */
27813     monthNames : Date.monthNames,
27814     /**
27815      * @cfg {Array} dayNames
27816      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27817      */
27818     dayNames : Date.dayNames,
27819     /**
27820      * @cfg {String} nextText
27821      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27822      */
27823     nextText: 'Next Month (Control+Right)',
27824     /**
27825      * @cfg {String} prevText
27826      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27827      */
27828     prevText: 'Previous Month (Control+Left)',
27829     /**
27830      * @cfg {String} monthYearText
27831      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27832      */
27833     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27834     /**
27835      * @cfg {Number} startDay
27836      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27837      */
27838     startDay : 0,
27839     /**
27840      * @cfg {Bool} showClear
27841      * Show a clear button (usefull for date form elements that can be blank.)
27842      */
27843     
27844     showClear: false,
27845     
27846     /**
27847      * Sets the value of the date field
27848      * @param {Date} value The date to set
27849      */
27850     setValue : function(value){
27851         var old = this.value;
27852         
27853         if (typeof(value) == 'string') {
27854          
27855             value = Date.parseDate(value, this.format);
27856         }
27857         if (!value) {
27858             value = new Date();
27859         }
27860         
27861         this.value = value.clearTime(true);
27862         if(this.el){
27863             this.update(this.value);
27864         }
27865     },
27866
27867     /**
27868      * Gets the current selected value of the date field
27869      * @return {Date} The selected date
27870      */
27871     getValue : function(){
27872         return this.value;
27873     },
27874
27875     // private
27876     focus : function(){
27877         if(this.el){
27878             this.update(this.activeDate);
27879         }
27880     },
27881
27882     // privateval
27883     onRender : function(container, position){
27884         
27885         var m = [
27886              '<table cellspacing="0">',
27887                 '<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>',
27888                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27889         var dn = this.dayNames;
27890         for(var i = 0; i < 7; i++){
27891             var d = this.startDay+i;
27892             if(d > 6){
27893                 d = d-7;
27894             }
27895             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27896         }
27897         m[m.length] = "</tr></thead><tbody><tr>";
27898         for(var i = 0; i < 42; i++) {
27899             if(i % 7 == 0 && i != 0){
27900                 m[m.length] = "</tr><tr>";
27901             }
27902             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27903         }
27904         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27905             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27906
27907         var el = document.createElement("div");
27908         el.className = "x-date-picker";
27909         el.innerHTML = m.join("");
27910
27911         container.dom.insertBefore(el, position);
27912
27913         this.el = Roo.get(el);
27914         this.eventEl = Roo.get(el.firstChild);
27915
27916         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27917             handler: this.showPrevMonth,
27918             scope: this,
27919             preventDefault:true,
27920             stopDefault:true
27921         });
27922
27923         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27924             handler: this.showNextMonth,
27925             scope: this,
27926             preventDefault:true,
27927             stopDefault:true
27928         });
27929
27930         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27931
27932         this.monthPicker = this.el.down('div.x-date-mp');
27933         this.monthPicker.enableDisplayMode('block');
27934         
27935         var kn = new Roo.KeyNav(this.eventEl, {
27936             "left" : function(e){
27937                 e.ctrlKey ?
27938                     this.showPrevMonth() :
27939                     this.update(this.activeDate.add("d", -1));
27940             },
27941
27942             "right" : function(e){
27943                 e.ctrlKey ?
27944                     this.showNextMonth() :
27945                     this.update(this.activeDate.add("d", 1));
27946             },
27947
27948             "up" : function(e){
27949                 e.ctrlKey ?
27950                     this.showNextYear() :
27951                     this.update(this.activeDate.add("d", -7));
27952             },
27953
27954             "down" : function(e){
27955                 e.ctrlKey ?
27956                     this.showPrevYear() :
27957                     this.update(this.activeDate.add("d", 7));
27958             },
27959
27960             "pageUp" : function(e){
27961                 this.showNextMonth();
27962             },
27963
27964             "pageDown" : function(e){
27965                 this.showPrevMonth();
27966             },
27967
27968             "enter" : function(e){
27969                 e.stopPropagation();
27970                 return true;
27971             },
27972
27973             scope : this
27974         });
27975
27976         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27977
27978         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27979
27980         this.el.unselectable();
27981         
27982         this.cells = this.el.select("table.x-date-inner tbody td");
27983         this.textNodes = this.el.query("table.x-date-inner tbody span");
27984
27985         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27986             text: "&#160;",
27987             tooltip: this.monthYearText
27988         });
27989
27990         this.mbtn.on('click', this.showMonthPicker, this);
27991         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27992
27993
27994         var today = (new Date()).dateFormat(this.format);
27995         
27996         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27997         if (this.showClear) {
27998             baseTb.add( new Roo.Toolbar.Fill());
27999         }
28000         baseTb.add({
28001             text: String.format(this.todayText, today),
28002             tooltip: String.format(this.todayTip, today),
28003             handler: this.selectToday,
28004             scope: this
28005         });
28006         
28007         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28008             
28009         //});
28010         if (this.showClear) {
28011             
28012             baseTb.add( new Roo.Toolbar.Fill());
28013             baseTb.add({
28014                 text: '&#160;',
28015                 cls: 'x-btn-icon x-btn-clear',
28016                 handler: function() {
28017                     //this.value = '';
28018                     this.fireEvent("select", this, '');
28019                 },
28020                 scope: this
28021             });
28022         }
28023         
28024         
28025         if(Roo.isIE){
28026             this.el.repaint();
28027         }
28028         this.update(this.value);
28029     },
28030
28031     createMonthPicker : function(){
28032         if(!this.monthPicker.dom.firstChild){
28033             var buf = ['<table border="0" cellspacing="0">'];
28034             for(var i = 0; i < 6; i++){
28035                 buf.push(
28036                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28037                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28038                     i == 0 ?
28039                     '<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>' :
28040                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28041                 );
28042             }
28043             buf.push(
28044                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28045                     this.okText,
28046                     '</button><button type="button" class="x-date-mp-cancel">',
28047                     this.cancelText,
28048                     '</button></td></tr>',
28049                 '</table>'
28050             );
28051             this.monthPicker.update(buf.join(''));
28052             this.monthPicker.on('click', this.onMonthClick, this);
28053             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28054
28055             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28056             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28057
28058             this.mpMonths.each(function(m, a, i){
28059                 i += 1;
28060                 if((i%2) == 0){
28061                     m.dom.xmonth = 5 + Math.round(i * .5);
28062                 }else{
28063                     m.dom.xmonth = Math.round((i-1) * .5);
28064                 }
28065             });
28066         }
28067     },
28068
28069     showMonthPicker : function(){
28070         this.createMonthPicker();
28071         var size = this.el.getSize();
28072         this.monthPicker.setSize(size);
28073         this.monthPicker.child('table').setSize(size);
28074
28075         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28076         this.updateMPMonth(this.mpSelMonth);
28077         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28078         this.updateMPYear(this.mpSelYear);
28079
28080         this.monthPicker.slideIn('t', {duration:.2});
28081     },
28082
28083     updateMPYear : function(y){
28084         this.mpyear = y;
28085         var ys = this.mpYears.elements;
28086         for(var i = 1; i <= 10; i++){
28087             var td = ys[i-1], y2;
28088             if((i%2) == 0){
28089                 y2 = y + Math.round(i * .5);
28090                 td.firstChild.innerHTML = y2;
28091                 td.xyear = y2;
28092             }else{
28093                 y2 = y - (5-Math.round(i * .5));
28094                 td.firstChild.innerHTML = y2;
28095                 td.xyear = y2;
28096             }
28097             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28098         }
28099     },
28100
28101     updateMPMonth : function(sm){
28102         this.mpMonths.each(function(m, a, i){
28103             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28104         });
28105     },
28106
28107     selectMPMonth: function(m){
28108         
28109     },
28110
28111     onMonthClick : function(e, t){
28112         e.stopEvent();
28113         var el = new Roo.Element(t), pn;
28114         if(el.is('button.x-date-mp-cancel')){
28115             this.hideMonthPicker();
28116         }
28117         else if(el.is('button.x-date-mp-ok')){
28118             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28119             this.hideMonthPicker();
28120         }
28121         else if(pn = el.up('td.x-date-mp-month', 2)){
28122             this.mpMonths.removeClass('x-date-mp-sel');
28123             pn.addClass('x-date-mp-sel');
28124             this.mpSelMonth = pn.dom.xmonth;
28125         }
28126         else if(pn = el.up('td.x-date-mp-year', 2)){
28127             this.mpYears.removeClass('x-date-mp-sel');
28128             pn.addClass('x-date-mp-sel');
28129             this.mpSelYear = pn.dom.xyear;
28130         }
28131         else if(el.is('a.x-date-mp-prev')){
28132             this.updateMPYear(this.mpyear-10);
28133         }
28134         else if(el.is('a.x-date-mp-next')){
28135             this.updateMPYear(this.mpyear+10);
28136         }
28137     },
28138
28139     onMonthDblClick : function(e, t){
28140         e.stopEvent();
28141         var el = new Roo.Element(t), pn;
28142         if(pn = el.up('td.x-date-mp-month', 2)){
28143             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28144             this.hideMonthPicker();
28145         }
28146         else if(pn = el.up('td.x-date-mp-year', 2)){
28147             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28148             this.hideMonthPicker();
28149         }
28150     },
28151
28152     hideMonthPicker : function(disableAnim){
28153         if(this.monthPicker){
28154             if(disableAnim === true){
28155                 this.monthPicker.hide();
28156             }else{
28157                 this.monthPicker.slideOut('t', {duration:.2});
28158             }
28159         }
28160     },
28161
28162     // private
28163     showPrevMonth : function(e){
28164         this.update(this.activeDate.add("mo", -1));
28165     },
28166
28167     // private
28168     showNextMonth : function(e){
28169         this.update(this.activeDate.add("mo", 1));
28170     },
28171
28172     // private
28173     showPrevYear : function(){
28174         this.update(this.activeDate.add("y", -1));
28175     },
28176
28177     // private
28178     showNextYear : function(){
28179         this.update(this.activeDate.add("y", 1));
28180     },
28181
28182     // private
28183     handleMouseWheel : function(e){
28184         var delta = e.getWheelDelta();
28185         if(delta > 0){
28186             this.showPrevMonth();
28187             e.stopEvent();
28188         } else if(delta < 0){
28189             this.showNextMonth();
28190             e.stopEvent();
28191         }
28192     },
28193
28194     // private
28195     handleDateClick : function(e, t){
28196         e.stopEvent();
28197         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28198             this.setValue(new Date(t.dateValue));
28199             this.fireEvent("select", this, this.value);
28200         }
28201     },
28202
28203     // private
28204     selectToday : function(){
28205         this.setValue(new Date().clearTime());
28206         this.fireEvent("select", this, this.value);
28207     },
28208
28209     // private
28210     update : function(date)
28211     {
28212         var vd = this.activeDate;
28213         this.activeDate = date;
28214         if(vd && this.el){
28215             var t = date.getTime();
28216             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28217                 this.cells.removeClass("x-date-selected");
28218                 this.cells.each(function(c){
28219                    if(c.dom.firstChild.dateValue == t){
28220                        c.addClass("x-date-selected");
28221                        setTimeout(function(){
28222                             try{c.dom.firstChild.focus();}catch(e){}
28223                        }, 50);
28224                        return false;
28225                    }
28226                 });
28227                 return;
28228             }
28229         }
28230         
28231         var days = date.getDaysInMonth();
28232         var firstOfMonth = date.getFirstDateOfMonth();
28233         var startingPos = firstOfMonth.getDay()-this.startDay;
28234
28235         if(startingPos <= this.startDay){
28236             startingPos += 7;
28237         }
28238
28239         var pm = date.add("mo", -1);
28240         var prevStart = pm.getDaysInMonth()-startingPos;
28241
28242         var cells = this.cells.elements;
28243         var textEls = this.textNodes;
28244         days += startingPos;
28245
28246         // convert everything to numbers so it's fast
28247         var day = 86400000;
28248         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28249         var today = new Date().clearTime().getTime();
28250         var sel = date.clearTime().getTime();
28251         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28252         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28253         var ddMatch = this.disabledDatesRE;
28254         var ddText = this.disabledDatesText;
28255         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28256         var ddaysText = this.disabledDaysText;
28257         var format = this.format;
28258
28259         var setCellClass = function(cal, cell){
28260             cell.title = "";
28261             var t = d.getTime();
28262             cell.firstChild.dateValue = t;
28263             if(t == today){
28264                 cell.className += " x-date-today";
28265                 cell.title = cal.todayText;
28266             }
28267             if(t == sel){
28268                 cell.className += " x-date-selected";
28269                 setTimeout(function(){
28270                     try{cell.firstChild.focus();}catch(e){}
28271                 }, 50);
28272             }
28273             // disabling
28274             if(t < min) {
28275                 cell.className = " x-date-disabled";
28276                 cell.title = cal.minText;
28277                 return;
28278             }
28279             if(t > max) {
28280                 cell.className = " x-date-disabled";
28281                 cell.title = cal.maxText;
28282                 return;
28283             }
28284             if(ddays){
28285                 if(ddays.indexOf(d.getDay()) != -1){
28286                     cell.title = ddaysText;
28287                     cell.className = " x-date-disabled";
28288                 }
28289             }
28290             if(ddMatch && format){
28291                 var fvalue = d.dateFormat(format);
28292                 if(ddMatch.test(fvalue)){
28293                     cell.title = ddText.replace("%0", fvalue);
28294                     cell.className = " x-date-disabled";
28295                 }
28296             }
28297         };
28298
28299         var i = 0;
28300         for(; i < startingPos; i++) {
28301             textEls[i].innerHTML = (++prevStart);
28302             d.setDate(d.getDate()+1);
28303             cells[i].className = "x-date-prevday";
28304             setCellClass(this, cells[i]);
28305         }
28306         for(; i < days; i++){
28307             intDay = i - startingPos + 1;
28308             textEls[i].innerHTML = (intDay);
28309             d.setDate(d.getDate()+1);
28310             cells[i].className = "x-date-active";
28311             setCellClass(this, cells[i]);
28312         }
28313         var extraDays = 0;
28314         for(; i < 42; i++) {
28315              textEls[i].innerHTML = (++extraDays);
28316              d.setDate(d.getDate()+1);
28317              cells[i].className = "x-date-nextday";
28318              setCellClass(this, cells[i]);
28319         }
28320
28321         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28322         this.fireEvent('monthchange', this, date);
28323         
28324         if(!this.internalRender){
28325             var main = this.el.dom.firstChild;
28326             var w = main.offsetWidth;
28327             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28328             Roo.fly(main).setWidth(w);
28329             this.internalRender = true;
28330             // opera does not respect the auto grow header center column
28331             // then, after it gets a width opera refuses to recalculate
28332             // without a second pass
28333             if(Roo.isOpera && !this.secondPass){
28334                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28335                 this.secondPass = true;
28336                 this.update.defer(10, this, [date]);
28337             }
28338         }
28339         
28340         
28341     }
28342 });        /*
28343  * Based on:
28344  * Ext JS Library 1.1.1
28345  * Copyright(c) 2006-2007, Ext JS, LLC.
28346  *
28347  * Originally Released Under LGPL - original licence link has changed is not relivant.
28348  *
28349  * Fork - LGPL
28350  * <script type="text/javascript">
28351  */
28352 /**
28353  * @class Roo.TabPanel
28354  * @extends Roo.util.Observable
28355  * A lightweight tab container.
28356  * <br><br>
28357  * Usage:
28358  * <pre><code>
28359 // basic tabs 1, built from existing content
28360 var tabs = new Roo.TabPanel("tabs1");
28361 tabs.addTab("script", "View Script");
28362 tabs.addTab("markup", "View Markup");
28363 tabs.activate("script");
28364
28365 // more advanced tabs, built from javascript
28366 var jtabs = new Roo.TabPanel("jtabs");
28367 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28368
28369 // set up the UpdateManager
28370 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28371 var updater = tab2.getUpdateManager();
28372 updater.setDefaultUrl("ajax1.htm");
28373 tab2.on('activate', updater.refresh, updater, true);
28374
28375 // Use setUrl for Ajax loading
28376 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28377 tab3.setUrl("ajax2.htm", null, true);
28378
28379 // Disabled tab
28380 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28381 tab4.disable();
28382
28383 jtabs.activate("jtabs-1");
28384  * </code></pre>
28385  * @constructor
28386  * Create a new TabPanel.
28387  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28388  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28389  */
28390 Roo.TabPanel = function(container, config){
28391     /**
28392     * The container element for this TabPanel.
28393     * @type Roo.Element
28394     */
28395     this.el = Roo.get(container, true);
28396     if(config){
28397         if(typeof config == "boolean"){
28398             this.tabPosition = config ? "bottom" : "top";
28399         }else{
28400             Roo.apply(this, config);
28401         }
28402     }
28403     if(this.tabPosition == "bottom"){
28404         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28405         this.el.addClass("x-tabs-bottom");
28406     }
28407     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28408     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28409     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28410     if(Roo.isIE){
28411         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28412     }
28413     if(this.tabPosition != "bottom"){
28414         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28415          * @type Roo.Element
28416          */
28417         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28418         this.el.addClass("x-tabs-top");
28419     }
28420     this.items = [];
28421
28422     this.bodyEl.setStyle("position", "relative");
28423
28424     this.active = null;
28425     this.activateDelegate = this.activate.createDelegate(this);
28426
28427     this.addEvents({
28428         /**
28429          * @event tabchange
28430          * Fires when the active tab changes
28431          * @param {Roo.TabPanel} this
28432          * @param {Roo.TabPanelItem} activePanel The new active tab
28433          */
28434         "tabchange": true,
28435         /**
28436          * @event beforetabchange
28437          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28438          * @param {Roo.TabPanel} this
28439          * @param {Object} e Set cancel to true on this object to cancel the tab change
28440          * @param {Roo.TabPanelItem} tab The tab being changed to
28441          */
28442         "beforetabchange" : true
28443     });
28444
28445     Roo.EventManager.onWindowResize(this.onResize, this);
28446     this.cpad = this.el.getPadding("lr");
28447     this.hiddenCount = 0;
28448
28449
28450     // toolbar on the tabbar support...
28451     if (this.toolbar) {
28452         var tcfg = this.toolbar;
28453         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28454         this.toolbar = new Roo.Toolbar(tcfg);
28455         if (Roo.isSafari) {
28456             var tbl = tcfg.container.child('table', true);
28457             tbl.setAttribute('width', '100%');
28458         }
28459         
28460     }
28461    
28462
28463
28464     Roo.TabPanel.superclass.constructor.call(this);
28465 };
28466
28467 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28468     /*
28469      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28470      */
28471     tabPosition : "top",
28472     /*
28473      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28474      */
28475     currentTabWidth : 0,
28476     /*
28477      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28478      */
28479     minTabWidth : 40,
28480     /*
28481      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28482      */
28483     maxTabWidth : 250,
28484     /*
28485      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28486      */
28487     preferredTabWidth : 175,
28488     /*
28489      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28490      */
28491     resizeTabs : false,
28492     /*
28493      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28494      */
28495     monitorResize : true,
28496     /*
28497      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28498      */
28499     toolbar : false,
28500
28501     /**
28502      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28503      * @param {String} id The id of the div to use <b>or create</b>
28504      * @param {String} text The text for the tab
28505      * @param {String} content (optional) Content to put in the TabPanelItem body
28506      * @param {Boolean} closable (optional) True to create a close icon on the tab
28507      * @return {Roo.TabPanelItem} The created TabPanelItem
28508      */
28509     addTab : function(id, text, content, closable){
28510         var item = new Roo.TabPanelItem(this, id, text, closable);
28511         this.addTabItem(item);
28512         if(content){
28513             item.setContent(content);
28514         }
28515         return item;
28516     },
28517
28518     /**
28519      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28520      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28521      * @return {Roo.TabPanelItem}
28522      */
28523     getTab : function(id){
28524         return this.items[id];
28525     },
28526
28527     /**
28528      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28529      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28530      */
28531     hideTab : function(id){
28532         var t = this.items[id];
28533         if(!t.isHidden()){
28534            t.setHidden(true);
28535            this.hiddenCount++;
28536            this.autoSizeTabs();
28537         }
28538     },
28539
28540     /**
28541      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28542      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28543      */
28544     unhideTab : function(id){
28545         var t = this.items[id];
28546         if(t.isHidden()){
28547            t.setHidden(false);
28548            this.hiddenCount--;
28549            this.autoSizeTabs();
28550         }
28551     },
28552
28553     /**
28554      * Adds an existing {@link Roo.TabPanelItem}.
28555      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28556      */
28557     addTabItem : function(item){
28558         this.items[item.id] = item;
28559         this.items.push(item);
28560         if(this.resizeTabs){
28561            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28562            this.autoSizeTabs();
28563         }else{
28564             item.autoSize();
28565         }
28566     },
28567
28568     /**
28569      * Removes a {@link Roo.TabPanelItem}.
28570      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28571      */
28572     removeTab : function(id){
28573         var items = this.items;
28574         var tab = items[id];
28575         if(!tab) { return; }
28576         var index = items.indexOf(tab);
28577         if(this.active == tab && items.length > 1){
28578             var newTab = this.getNextAvailable(index);
28579             if(newTab) {
28580                 newTab.activate();
28581             }
28582         }
28583         this.stripEl.dom.removeChild(tab.pnode.dom);
28584         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28585             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28586         }
28587         items.splice(index, 1);
28588         delete this.items[tab.id];
28589         tab.fireEvent("close", tab);
28590         tab.purgeListeners();
28591         this.autoSizeTabs();
28592     },
28593
28594     getNextAvailable : function(start){
28595         var items = this.items;
28596         var index = start;
28597         // look for a next tab that will slide over to
28598         // replace the one being removed
28599         while(index < items.length){
28600             var item = items[++index];
28601             if(item && !item.isHidden()){
28602                 return item;
28603             }
28604         }
28605         // if one isn't found select the previous tab (on the left)
28606         index = start;
28607         while(index >= 0){
28608             var item = items[--index];
28609             if(item && !item.isHidden()){
28610                 return item;
28611             }
28612         }
28613         return null;
28614     },
28615
28616     /**
28617      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28618      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28619      */
28620     disableTab : function(id){
28621         var tab = this.items[id];
28622         if(tab && this.active != tab){
28623             tab.disable();
28624         }
28625     },
28626
28627     /**
28628      * Enables a {@link Roo.TabPanelItem} that is disabled.
28629      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28630      */
28631     enableTab : function(id){
28632         var tab = this.items[id];
28633         tab.enable();
28634     },
28635
28636     /**
28637      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28638      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28639      * @return {Roo.TabPanelItem} The TabPanelItem.
28640      */
28641     activate : function(id){
28642         var tab = this.items[id];
28643         if(!tab){
28644             return null;
28645         }
28646         if(tab == this.active || tab.disabled){
28647             return tab;
28648         }
28649         var e = {};
28650         this.fireEvent("beforetabchange", this, e, tab);
28651         if(e.cancel !== true && !tab.disabled){
28652             if(this.active){
28653                 this.active.hide();
28654             }
28655             this.active = this.items[id];
28656             this.active.show();
28657             this.fireEvent("tabchange", this, this.active);
28658         }
28659         return tab;
28660     },
28661
28662     /**
28663      * Gets the active {@link Roo.TabPanelItem}.
28664      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28665      */
28666     getActiveTab : function(){
28667         return this.active;
28668     },
28669
28670     /**
28671      * Updates the tab body element to fit the height of the container element
28672      * for overflow scrolling
28673      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28674      */
28675     syncHeight : function(targetHeight){
28676         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28677         var bm = this.bodyEl.getMargins();
28678         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28679         this.bodyEl.setHeight(newHeight);
28680         return newHeight;
28681     },
28682
28683     onResize : function(){
28684         if(this.monitorResize){
28685             this.autoSizeTabs();
28686         }
28687     },
28688
28689     /**
28690      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28691      */
28692     beginUpdate : function(){
28693         this.updating = true;
28694     },
28695
28696     /**
28697      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28698      */
28699     endUpdate : function(){
28700         this.updating = false;
28701         this.autoSizeTabs();
28702     },
28703
28704     /**
28705      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28706      */
28707     autoSizeTabs : function(){
28708         var count = this.items.length;
28709         var vcount = count - this.hiddenCount;
28710         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28711             return;
28712         }
28713         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28714         var availWidth = Math.floor(w / vcount);
28715         var b = this.stripBody;
28716         if(b.getWidth() > w){
28717             var tabs = this.items;
28718             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28719             if(availWidth < this.minTabWidth){
28720                 /*if(!this.sleft){    // incomplete scrolling code
28721                     this.createScrollButtons();
28722                 }
28723                 this.showScroll();
28724                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28725             }
28726         }else{
28727             if(this.currentTabWidth < this.preferredTabWidth){
28728                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28729             }
28730         }
28731     },
28732
28733     /**
28734      * Returns the number of tabs in this TabPanel.
28735      * @return {Number}
28736      */
28737      getCount : function(){
28738          return this.items.length;
28739      },
28740
28741     /**
28742      * Resizes all the tabs to the passed width
28743      * @param {Number} The new width
28744      */
28745     setTabWidth : function(width){
28746         this.currentTabWidth = width;
28747         for(var i = 0, len = this.items.length; i < len; i++) {
28748                 if(!this.items[i].isHidden()) {
28749                 this.items[i].setWidth(width);
28750             }
28751         }
28752     },
28753
28754     /**
28755      * Destroys this TabPanel
28756      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28757      */
28758     destroy : function(removeEl){
28759         Roo.EventManager.removeResizeListener(this.onResize, this);
28760         for(var i = 0, len = this.items.length; i < len; i++){
28761             this.items[i].purgeListeners();
28762         }
28763         if(removeEl === true){
28764             this.el.update("");
28765             this.el.remove();
28766         }
28767     }
28768 });
28769
28770 /**
28771  * @class Roo.TabPanelItem
28772  * @extends Roo.util.Observable
28773  * Represents an individual item (tab plus body) in a TabPanel.
28774  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28775  * @param {String} id The id of this TabPanelItem
28776  * @param {String} text The text for the tab of this TabPanelItem
28777  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28778  */
28779 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28780     /**
28781      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28782      * @type Roo.TabPanel
28783      */
28784     this.tabPanel = tabPanel;
28785     /**
28786      * The id for this TabPanelItem
28787      * @type String
28788      */
28789     this.id = id;
28790     /** @private */
28791     this.disabled = false;
28792     /** @private */
28793     this.text = text;
28794     /** @private */
28795     this.loaded = false;
28796     this.closable = closable;
28797
28798     /**
28799      * The body element for this TabPanelItem.
28800      * @type Roo.Element
28801      */
28802     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28803     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28804     this.bodyEl.setStyle("display", "block");
28805     this.bodyEl.setStyle("zoom", "1");
28806     this.hideAction();
28807
28808     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28809     /** @private */
28810     this.el = Roo.get(els.el, true);
28811     this.inner = Roo.get(els.inner, true);
28812     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28813     this.pnode = Roo.get(els.el.parentNode, true);
28814     this.el.on("mousedown", this.onTabMouseDown, this);
28815     this.el.on("click", this.onTabClick, this);
28816     /** @private */
28817     if(closable){
28818         var c = Roo.get(els.close, true);
28819         c.dom.title = this.closeText;
28820         c.addClassOnOver("close-over");
28821         c.on("click", this.closeClick, this);
28822      }
28823
28824     this.addEvents({
28825          /**
28826          * @event activate
28827          * Fires when this tab becomes the active tab.
28828          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28829          * @param {Roo.TabPanelItem} this
28830          */
28831         "activate": true,
28832         /**
28833          * @event beforeclose
28834          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28835          * @param {Roo.TabPanelItem} this
28836          * @param {Object} e Set cancel to true on this object to cancel the close.
28837          */
28838         "beforeclose": true,
28839         /**
28840          * @event close
28841          * Fires when this tab is closed.
28842          * @param {Roo.TabPanelItem} this
28843          */
28844          "close": true,
28845         /**
28846          * @event deactivate
28847          * Fires when this tab is no longer the active tab.
28848          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28849          * @param {Roo.TabPanelItem} this
28850          */
28851          "deactivate" : true
28852     });
28853     this.hidden = false;
28854
28855     Roo.TabPanelItem.superclass.constructor.call(this);
28856 };
28857
28858 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28859     purgeListeners : function(){
28860        Roo.util.Observable.prototype.purgeListeners.call(this);
28861        this.el.removeAllListeners();
28862     },
28863     /**
28864      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28865      */
28866     show : function(){
28867         this.pnode.addClass("on");
28868         this.showAction();
28869         if(Roo.isOpera){
28870             this.tabPanel.stripWrap.repaint();
28871         }
28872         this.fireEvent("activate", this.tabPanel, this);
28873     },
28874
28875     /**
28876      * Returns true if this tab is the active tab.
28877      * @return {Boolean}
28878      */
28879     isActive : function(){
28880         return this.tabPanel.getActiveTab() == this;
28881     },
28882
28883     /**
28884      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28885      */
28886     hide : function(){
28887         this.pnode.removeClass("on");
28888         this.hideAction();
28889         this.fireEvent("deactivate", this.tabPanel, this);
28890     },
28891
28892     hideAction : function(){
28893         this.bodyEl.hide();
28894         this.bodyEl.setStyle("position", "absolute");
28895         this.bodyEl.setLeft("-20000px");
28896         this.bodyEl.setTop("-20000px");
28897     },
28898
28899     showAction : function(){
28900         this.bodyEl.setStyle("position", "relative");
28901         this.bodyEl.setTop("");
28902         this.bodyEl.setLeft("");
28903         this.bodyEl.show();
28904     },
28905
28906     /**
28907      * Set the tooltip for the tab.
28908      * @param {String} tooltip The tab's tooltip
28909      */
28910     setTooltip : function(text){
28911         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28912             this.textEl.dom.qtip = text;
28913             this.textEl.dom.removeAttribute('title');
28914         }else{
28915             this.textEl.dom.title = text;
28916         }
28917     },
28918
28919     onTabClick : function(e){
28920         e.preventDefault();
28921         this.tabPanel.activate(this.id);
28922     },
28923
28924     onTabMouseDown : function(e){
28925         e.preventDefault();
28926         this.tabPanel.activate(this.id);
28927     },
28928
28929     getWidth : function(){
28930         return this.inner.getWidth();
28931     },
28932
28933     setWidth : function(width){
28934         var iwidth = width - this.pnode.getPadding("lr");
28935         this.inner.setWidth(iwidth);
28936         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28937         this.pnode.setWidth(width);
28938     },
28939
28940     /**
28941      * Show or hide the tab
28942      * @param {Boolean} hidden True to hide or false to show.
28943      */
28944     setHidden : function(hidden){
28945         this.hidden = hidden;
28946         this.pnode.setStyle("display", hidden ? "none" : "");
28947     },
28948
28949     /**
28950      * Returns true if this tab is "hidden"
28951      * @return {Boolean}
28952      */
28953     isHidden : function(){
28954         return this.hidden;
28955     },
28956
28957     /**
28958      * Returns the text for this tab
28959      * @return {String}
28960      */
28961     getText : function(){
28962         return this.text;
28963     },
28964
28965     autoSize : function(){
28966         //this.el.beginMeasure();
28967         this.textEl.setWidth(1);
28968         /*
28969          *  #2804 [new] Tabs in Roojs
28970          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28971          */
28972         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28973         //this.el.endMeasure();
28974     },
28975
28976     /**
28977      * Sets the text for the tab (Note: this also sets the tooltip text)
28978      * @param {String} text The tab's text and tooltip
28979      */
28980     setText : function(text){
28981         this.text = text;
28982         this.textEl.update(text);
28983         this.setTooltip(text);
28984         if(!this.tabPanel.resizeTabs){
28985             this.autoSize();
28986         }
28987     },
28988     /**
28989      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28990      */
28991     activate : function(){
28992         this.tabPanel.activate(this.id);
28993     },
28994
28995     /**
28996      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28997      */
28998     disable : function(){
28999         if(this.tabPanel.active != this){
29000             this.disabled = true;
29001             this.pnode.addClass("disabled");
29002         }
29003     },
29004
29005     /**
29006      * Enables this TabPanelItem if it was previously disabled.
29007      */
29008     enable : function(){
29009         this.disabled = false;
29010         this.pnode.removeClass("disabled");
29011     },
29012
29013     /**
29014      * Sets the content for this TabPanelItem.
29015      * @param {String} content The content
29016      * @param {Boolean} loadScripts true to look for and load scripts
29017      */
29018     setContent : function(content, loadScripts){
29019         this.bodyEl.update(content, loadScripts);
29020     },
29021
29022     /**
29023      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29024      * @return {Roo.UpdateManager} The UpdateManager
29025      */
29026     getUpdateManager : function(){
29027         return this.bodyEl.getUpdateManager();
29028     },
29029
29030     /**
29031      * Set a URL to be used to load the content for this TabPanelItem.
29032      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29033      * @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)
29034      * @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)
29035      * @return {Roo.UpdateManager} The UpdateManager
29036      */
29037     setUrl : function(url, params, loadOnce){
29038         if(this.refreshDelegate){
29039             this.un('activate', this.refreshDelegate);
29040         }
29041         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29042         this.on("activate", this.refreshDelegate);
29043         return this.bodyEl.getUpdateManager();
29044     },
29045
29046     /** @private */
29047     _handleRefresh : function(url, params, loadOnce){
29048         if(!loadOnce || !this.loaded){
29049             var updater = this.bodyEl.getUpdateManager();
29050             updater.update(url, params, this._setLoaded.createDelegate(this));
29051         }
29052     },
29053
29054     /**
29055      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29056      *   Will fail silently if the setUrl method has not been called.
29057      *   This does not activate the panel, just updates its content.
29058      */
29059     refresh : function(){
29060         if(this.refreshDelegate){
29061            this.loaded = false;
29062            this.refreshDelegate();
29063         }
29064     },
29065
29066     /** @private */
29067     _setLoaded : function(){
29068         this.loaded = true;
29069     },
29070
29071     /** @private */
29072     closeClick : function(e){
29073         var o = {};
29074         e.stopEvent();
29075         this.fireEvent("beforeclose", this, o);
29076         if(o.cancel !== true){
29077             this.tabPanel.removeTab(this.id);
29078         }
29079     },
29080     /**
29081      * The text displayed in the tooltip for the close icon.
29082      * @type String
29083      */
29084     closeText : "Close this tab"
29085 });
29086
29087 /** @private */
29088 Roo.TabPanel.prototype.createStrip = function(container){
29089     var strip = document.createElement("div");
29090     strip.className = "x-tabs-wrap";
29091     container.appendChild(strip);
29092     return strip;
29093 };
29094 /** @private */
29095 Roo.TabPanel.prototype.createStripList = function(strip){
29096     // div wrapper for retard IE
29097     // returns the "tr" element.
29098     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29099         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29100         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29101     return strip.firstChild.firstChild.firstChild.firstChild;
29102 };
29103 /** @private */
29104 Roo.TabPanel.prototype.createBody = function(container){
29105     var body = document.createElement("div");
29106     Roo.id(body, "tab-body");
29107     Roo.fly(body).addClass("x-tabs-body");
29108     container.appendChild(body);
29109     return body;
29110 };
29111 /** @private */
29112 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29113     var body = Roo.getDom(id);
29114     if(!body){
29115         body = document.createElement("div");
29116         body.id = id;
29117     }
29118     Roo.fly(body).addClass("x-tabs-item-body");
29119     bodyEl.insertBefore(body, bodyEl.firstChild);
29120     return body;
29121 };
29122 /** @private */
29123 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29124     var td = document.createElement("td");
29125     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29126     //stripEl.appendChild(td);
29127     if(closable){
29128         td.className = "x-tabs-closable";
29129         if(!this.closeTpl){
29130             this.closeTpl = new Roo.Template(
29131                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29132                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29133                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29134             );
29135         }
29136         var el = this.closeTpl.overwrite(td, {"text": text});
29137         var close = el.getElementsByTagName("div")[0];
29138         var inner = el.getElementsByTagName("em")[0];
29139         return {"el": el, "close": close, "inner": inner};
29140     } else {
29141         if(!this.tabTpl){
29142             this.tabTpl = new Roo.Template(
29143                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29144                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29145             );
29146         }
29147         var el = this.tabTpl.overwrite(td, {"text": text});
29148         var inner = el.getElementsByTagName("em")[0];
29149         return {"el": el, "inner": inner};
29150     }
29151 };/*
29152  * Based on:
29153  * Ext JS Library 1.1.1
29154  * Copyright(c) 2006-2007, Ext JS, LLC.
29155  *
29156  * Originally Released Under LGPL - original licence link has changed is not relivant.
29157  *
29158  * Fork - LGPL
29159  * <script type="text/javascript">
29160  */
29161
29162 /**
29163  * @class Roo.Button
29164  * @extends Roo.util.Observable
29165  * Simple Button class
29166  * @cfg {String} text The button text
29167  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29168  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29169  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29170  * @cfg {Object} scope The scope of the handler
29171  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29172  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29173  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29174  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29175  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29176  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29177    applies if enableToggle = true)
29178  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29179  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29180   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29181  * @constructor
29182  * Create a new button
29183  * @param {Object} config The config object
29184  */
29185 Roo.Button = function(renderTo, config)
29186 {
29187     if (!config) {
29188         config = renderTo;
29189         renderTo = config.renderTo || false;
29190     }
29191     
29192     Roo.apply(this, config);
29193     this.addEvents({
29194         /**
29195              * @event click
29196              * Fires when this button is clicked
29197              * @param {Button} this
29198              * @param {EventObject} e The click event
29199              */
29200             "click" : true,
29201         /**
29202              * @event toggle
29203              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29204              * @param {Button} this
29205              * @param {Boolean} pressed
29206              */
29207             "toggle" : true,
29208         /**
29209              * @event mouseover
29210              * Fires when the mouse hovers over the button
29211              * @param {Button} this
29212              * @param {Event} e The event object
29213              */
29214         'mouseover' : true,
29215         /**
29216              * @event mouseout
29217              * Fires when the mouse exits the button
29218              * @param {Button} this
29219              * @param {Event} e The event object
29220              */
29221         'mouseout': true,
29222          /**
29223              * @event render
29224              * Fires when the button is rendered
29225              * @param {Button} this
29226              */
29227         'render': true
29228     });
29229     if(this.menu){
29230         this.menu = Roo.menu.MenuMgr.get(this.menu);
29231     }
29232     // register listeners first!!  - so render can be captured..
29233     Roo.util.Observable.call(this);
29234     if(renderTo){
29235         this.render(renderTo);
29236     }
29237     
29238   
29239 };
29240
29241 Roo.extend(Roo.Button, Roo.util.Observable, {
29242     /**
29243      * 
29244      */
29245     
29246     /**
29247      * Read-only. True if this button is hidden
29248      * @type Boolean
29249      */
29250     hidden : false,
29251     /**
29252      * Read-only. True if this button is disabled
29253      * @type Boolean
29254      */
29255     disabled : false,
29256     /**
29257      * Read-only. True if this button is pressed (only if enableToggle = true)
29258      * @type Boolean
29259      */
29260     pressed : false,
29261
29262     /**
29263      * @cfg {Number} tabIndex 
29264      * The DOM tabIndex for this button (defaults to undefined)
29265      */
29266     tabIndex : undefined,
29267
29268     /**
29269      * @cfg {Boolean} enableToggle
29270      * True to enable pressed/not pressed toggling (defaults to false)
29271      */
29272     enableToggle: false,
29273     /**
29274      * @cfg {Mixed} menu
29275      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29276      */
29277     menu : undefined,
29278     /**
29279      * @cfg {String} menuAlign
29280      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29281      */
29282     menuAlign : "tl-bl?",
29283
29284     /**
29285      * @cfg {String} iconCls
29286      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29287      */
29288     iconCls : undefined,
29289     /**
29290      * @cfg {String} type
29291      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29292      */
29293     type : 'button',
29294
29295     // private
29296     menuClassTarget: 'tr',
29297
29298     /**
29299      * @cfg {String} clickEvent
29300      * The type of event to map to the button's event handler (defaults to 'click')
29301      */
29302     clickEvent : 'click',
29303
29304     /**
29305      * @cfg {Boolean} handleMouseEvents
29306      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29307      */
29308     handleMouseEvents : true,
29309
29310     /**
29311      * @cfg {String} tooltipType
29312      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29313      */
29314     tooltipType : 'qtip',
29315
29316     /**
29317      * @cfg {String} cls
29318      * A CSS class to apply to the button's main element.
29319      */
29320     
29321     /**
29322      * @cfg {Roo.Template} template (Optional)
29323      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29324      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29325      * require code modifications if required elements (e.g. a button) aren't present.
29326      */
29327
29328     // private
29329     render : function(renderTo){
29330         var btn;
29331         if(this.hideParent){
29332             this.parentEl = Roo.get(renderTo);
29333         }
29334         if(!this.dhconfig){
29335             if(!this.template){
29336                 if(!Roo.Button.buttonTemplate){
29337                     // hideous table template
29338                     Roo.Button.buttonTemplate = new Roo.Template(
29339                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29340                         '<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>',
29341                         "</tr></tbody></table>");
29342                 }
29343                 this.template = Roo.Button.buttonTemplate;
29344             }
29345             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29346             var btnEl = btn.child("button:first");
29347             btnEl.on('focus', this.onFocus, this);
29348             btnEl.on('blur', this.onBlur, this);
29349             if(this.cls){
29350                 btn.addClass(this.cls);
29351             }
29352             if(this.icon){
29353                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29354             }
29355             if(this.iconCls){
29356                 btnEl.addClass(this.iconCls);
29357                 if(!this.cls){
29358                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29359                 }
29360             }
29361             if(this.tabIndex !== undefined){
29362                 btnEl.dom.tabIndex = this.tabIndex;
29363             }
29364             if(this.tooltip){
29365                 if(typeof this.tooltip == 'object'){
29366                     Roo.QuickTips.tips(Roo.apply({
29367                           target: btnEl.id
29368                     }, this.tooltip));
29369                 } else {
29370                     btnEl.dom[this.tooltipType] = this.tooltip;
29371                 }
29372             }
29373         }else{
29374             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29375         }
29376         this.el = btn;
29377         if(this.id){
29378             this.el.dom.id = this.el.id = this.id;
29379         }
29380         if(this.menu){
29381             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29382             this.menu.on("show", this.onMenuShow, this);
29383             this.menu.on("hide", this.onMenuHide, this);
29384         }
29385         btn.addClass("x-btn");
29386         if(Roo.isIE && !Roo.isIE7){
29387             this.autoWidth.defer(1, this);
29388         }else{
29389             this.autoWidth();
29390         }
29391         if(this.handleMouseEvents){
29392             btn.on("mouseover", this.onMouseOver, this);
29393             btn.on("mouseout", this.onMouseOut, this);
29394             btn.on("mousedown", this.onMouseDown, this);
29395         }
29396         btn.on(this.clickEvent, this.onClick, this);
29397         //btn.on("mouseup", this.onMouseUp, this);
29398         if(this.hidden){
29399             this.hide();
29400         }
29401         if(this.disabled){
29402             this.disable();
29403         }
29404         Roo.ButtonToggleMgr.register(this);
29405         if(this.pressed){
29406             this.el.addClass("x-btn-pressed");
29407         }
29408         if(this.repeat){
29409             var repeater = new Roo.util.ClickRepeater(btn,
29410                 typeof this.repeat == "object" ? this.repeat : {}
29411             );
29412             repeater.on("click", this.onClick,  this);
29413         }
29414         
29415         this.fireEvent('render', this);
29416         
29417     },
29418     /**
29419      * Returns the button's underlying element
29420      * @return {Roo.Element} The element
29421      */
29422     getEl : function(){
29423         return this.el;  
29424     },
29425     
29426     /**
29427      * Destroys this Button and removes any listeners.
29428      */
29429     destroy : function(){
29430         Roo.ButtonToggleMgr.unregister(this);
29431         this.el.removeAllListeners();
29432         this.purgeListeners();
29433         this.el.remove();
29434     },
29435
29436     // private
29437     autoWidth : function(){
29438         if(this.el){
29439             this.el.setWidth("auto");
29440             if(Roo.isIE7 && Roo.isStrict){
29441                 var ib = this.el.child('button');
29442                 if(ib && ib.getWidth() > 20){
29443                     ib.clip();
29444                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29445                 }
29446             }
29447             if(this.minWidth){
29448                 if(this.hidden){
29449                     this.el.beginMeasure();
29450                 }
29451                 if(this.el.getWidth() < this.minWidth){
29452                     this.el.setWidth(this.minWidth);
29453                 }
29454                 if(this.hidden){
29455                     this.el.endMeasure();
29456                 }
29457             }
29458         }
29459     },
29460
29461     /**
29462      * Assigns this button's click handler
29463      * @param {Function} handler The function to call when the button is clicked
29464      * @param {Object} scope (optional) Scope for the function passed in
29465      */
29466     setHandler : function(handler, scope){
29467         this.handler = handler;
29468         this.scope = scope;  
29469     },
29470     
29471     /**
29472      * Sets this button's text
29473      * @param {String} text The button text
29474      */
29475     setText : function(text){
29476         this.text = text;
29477         if(this.el){
29478             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29479         }
29480         this.autoWidth();
29481     },
29482     
29483     /**
29484      * Gets the text for this button
29485      * @return {String} The button text
29486      */
29487     getText : function(){
29488         return this.text;  
29489     },
29490     
29491     /**
29492      * Show this button
29493      */
29494     show: function(){
29495         this.hidden = false;
29496         if(this.el){
29497             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29498         }
29499     },
29500     
29501     /**
29502      * Hide this button
29503      */
29504     hide: function(){
29505         this.hidden = true;
29506         if(this.el){
29507             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29508         }
29509     },
29510     
29511     /**
29512      * Convenience function for boolean show/hide
29513      * @param {Boolean} visible True to show, false to hide
29514      */
29515     setVisible: function(visible){
29516         if(visible) {
29517             this.show();
29518         }else{
29519             this.hide();
29520         }
29521     },
29522     
29523     /**
29524      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29525      * @param {Boolean} state (optional) Force a particular state
29526      */
29527     toggle : function(state){
29528         state = state === undefined ? !this.pressed : state;
29529         if(state != this.pressed){
29530             if(state){
29531                 this.el.addClass("x-btn-pressed");
29532                 this.pressed = true;
29533                 this.fireEvent("toggle", this, true);
29534             }else{
29535                 this.el.removeClass("x-btn-pressed");
29536                 this.pressed = false;
29537                 this.fireEvent("toggle", this, false);
29538             }
29539             if(this.toggleHandler){
29540                 this.toggleHandler.call(this.scope || this, this, state);
29541             }
29542         }
29543     },
29544     
29545     /**
29546      * Focus the button
29547      */
29548     focus : function(){
29549         this.el.child('button:first').focus();
29550     },
29551     
29552     /**
29553      * Disable this button
29554      */
29555     disable : function(){
29556         if(this.el){
29557             this.el.addClass("x-btn-disabled");
29558         }
29559         this.disabled = true;
29560     },
29561     
29562     /**
29563      * Enable this button
29564      */
29565     enable : function(){
29566         if(this.el){
29567             this.el.removeClass("x-btn-disabled");
29568         }
29569         this.disabled = false;
29570     },
29571
29572     /**
29573      * Convenience function for boolean enable/disable
29574      * @param {Boolean} enabled True to enable, false to disable
29575      */
29576     setDisabled : function(v){
29577         this[v !== true ? "enable" : "disable"]();
29578     },
29579
29580     // private
29581     onClick : function(e)
29582     {
29583         if(e){
29584             e.preventDefault();
29585         }
29586         if(e.button != 0){
29587             return;
29588         }
29589         if(!this.disabled){
29590             if(this.enableToggle){
29591                 this.toggle();
29592             }
29593             if(this.menu && !this.menu.isVisible()){
29594                 this.menu.show(this.el, this.menuAlign);
29595             }
29596             this.fireEvent("click", this, e);
29597             if(this.handler){
29598                 this.el.removeClass("x-btn-over");
29599                 this.handler.call(this.scope || this, this, e);
29600             }
29601         }
29602     },
29603     // private
29604     onMouseOver : function(e){
29605         if(!this.disabled){
29606             this.el.addClass("x-btn-over");
29607             this.fireEvent('mouseover', this, e);
29608         }
29609     },
29610     // private
29611     onMouseOut : function(e){
29612         if(!e.within(this.el,  true)){
29613             this.el.removeClass("x-btn-over");
29614             this.fireEvent('mouseout', this, e);
29615         }
29616     },
29617     // private
29618     onFocus : function(e){
29619         if(!this.disabled){
29620             this.el.addClass("x-btn-focus");
29621         }
29622     },
29623     // private
29624     onBlur : function(e){
29625         this.el.removeClass("x-btn-focus");
29626     },
29627     // private
29628     onMouseDown : function(e){
29629         if(!this.disabled && e.button == 0){
29630             this.el.addClass("x-btn-click");
29631             Roo.get(document).on('mouseup', this.onMouseUp, this);
29632         }
29633     },
29634     // private
29635     onMouseUp : function(e){
29636         if(e.button == 0){
29637             this.el.removeClass("x-btn-click");
29638             Roo.get(document).un('mouseup', this.onMouseUp, this);
29639         }
29640     },
29641     // private
29642     onMenuShow : function(e){
29643         this.el.addClass("x-btn-menu-active");
29644     },
29645     // private
29646     onMenuHide : function(e){
29647         this.el.removeClass("x-btn-menu-active");
29648     }   
29649 });
29650
29651 // Private utility class used by Button
29652 Roo.ButtonToggleMgr = function(){
29653    var groups = {};
29654    
29655    function toggleGroup(btn, state){
29656        if(state){
29657            var g = groups[btn.toggleGroup];
29658            for(var i = 0, l = g.length; i < l; i++){
29659                if(g[i] != btn){
29660                    g[i].toggle(false);
29661                }
29662            }
29663        }
29664    }
29665    
29666    return {
29667        register : function(btn){
29668            if(!btn.toggleGroup){
29669                return;
29670            }
29671            var g = groups[btn.toggleGroup];
29672            if(!g){
29673                g = groups[btn.toggleGroup] = [];
29674            }
29675            g.push(btn);
29676            btn.on("toggle", toggleGroup);
29677        },
29678        
29679        unregister : function(btn){
29680            if(!btn.toggleGroup){
29681                return;
29682            }
29683            var g = groups[btn.toggleGroup];
29684            if(g){
29685                g.remove(btn);
29686                btn.un("toggle", toggleGroup);
29687            }
29688        }
29689    };
29690 }();/*
29691  * Based on:
29692  * Ext JS Library 1.1.1
29693  * Copyright(c) 2006-2007, Ext JS, LLC.
29694  *
29695  * Originally Released Under LGPL - original licence link has changed is not relivant.
29696  *
29697  * Fork - LGPL
29698  * <script type="text/javascript">
29699  */
29700  
29701 /**
29702  * @class Roo.SplitButton
29703  * @extends Roo.Button
29704  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29705  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29706  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29707  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29708  * @cfg {String} arrowTooltip The title attribute of the arrow
29709  * @constructor
29710  * Create a new menu button
29711  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29712  * @param {Object} config The config object
29713  */
29714 Roo.SplitButton = function(renderTo, config){
29715     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29716     /**
29717      * @event arrowclick
29718      * Fires when this button's arrow is clicked
29719      * @param {SplitButton} this
29720      * @param {EventObject} e The click event
29721      */
29722     this.addEvents({"arrowclick":true});
29723 };
29724
29725 Roo.extend(Roo.SplitButton, Roo.Button, {
29726     render : function(renderTo){
29727         // this is one sweet looking template!
29728         var tpl = new Roo.Template(
29729             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29730             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29731             '<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>',
29732             "</tbody></table></td><td>",
29733             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29734             '<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>',
29735             "</tbody></table></td></tr></table>"
29736         );
29737         var btn = tpl.append(renderTo, [this.text, this.type], true);
29738         var btnEl = btn.child("button");
29739         if(this.cls){
29740             btn.addClass(this.cls);
29741         }
29742         if(this.icon){
29743             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29744         }
29745         if(this.iconCls){
29746             btnEl.addClass(this.iconCls);
29747             if(!this.cls){
29748                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29749             }
29750         }
29751         this.el = btn;
29752         if(this.handleMouseEvents){
29753             btn.on("mouseover", this.onMouseOver, this);
29754             btn.on("mouseout", this.onMouseOut, this);
29755             btn.on("mousedown", this.onMouseDown, this);
29756             btn.on("mouseup", this.onMouseUp, this);
29757         }
29758         btn.on(this.clickEvent, this.onClick, this);
29759         if(this.tooltip){
29760             if(typeof this.tooltip == 'object'){
29761                 Roo.QuickTips.tips(Roo.apply({
29762                       target: btnEl.id
29763                 }, this.tooltip));
29764             } else {
29765                 btnEl.dom[this.tooltipType] = this.tooltip;
29766             }
29767         }
29768         if(this.arrowTooltip){
29769             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29770         }
29771         if(this.hidden){
29772             this.hide();
29773         }
29774         if(this.disabled){
29775             this.disable();
29776         }
29777         if(this.pressed){
29778             this.el.addClass("x-btn-pressed");
29779         }
29780         if(Roo.isIE && !Roo.isIE7){
29781             this.autoWidth.defer(1, this);
29782         }else{
29783             this.autoWidth();
29784         }
29785         if(this.menu){
29786             this.menu.on("show", this.onMenuShow, this);
29787             this.menu.on("hide", this.onMenuHide, this);
29788         }
29789         this.fireEvent('render', this);
29790     },
29791
29792     // private
29793     autoWidth : function(){
29794         if(this.el){
29795             var tbl = this.el.child("table:first");
29796             var tbl2 = this.el.child("table:last");
29797             this.el.setWidth("auto");
29798             tbl.setWidth("auto");
29799             if(Roo.isIE7 && Roo.isStrict){
29800                 var ib = this.el.child('button:first');
29801                 if(ib && ib.getWidth() > 20){
29802                     ib.clip();
29803                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29804                 }
29805             }
29806             if(this.minWidth){
29807                 if(this.hidden){
29808                     this.el.beginMeasure();
29809                 }
29810                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29811                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29812                 }
29813                 if(this.hidden){
29814                     this.el.endMeasure();
29815                 }
29816             }
29817             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29818         } 
29819     },
29820     /**
29821      * Sets this button's click handler
29822      * @param {Function} handler The function to call when the button is clicked
29823      * @param {Object} scope (optional) Scope for the function passed above
29824      */
29825     setHandler : function(handler, scope){
29826         this.handler = handler;
29827         this.scope = scope;  
29828     },
29829     
29830     /**
29831      * Sets this button's arrow click handler
29832      * @param {Function} handler The function to call when the arrow is clicked
29833      * @param {Object} scope (optional) Scope for the function passed above
29834      */
29835     setArrowHandler : function(handler, scope){
29836         this.arrowHandler = handler;
29837         this.scope = scope;  
29838     },
29839     
29840     /**
29841      * Focus the button
29842      */
29843     focus : function(){
29844         if(this.el){
29845             this.el.child("button:first").focus();
29846         }
29847     },
29848
29849     // private
29850     onClick : function(e){
29851         e.preventDefault();
29852         if(!this.disabled){
29853             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29854                 if(this.menu && !this.menu.isVisible()){
29855                     this.menu.show(this.el, this.menuAlign);
29856                 }
29857                 this.fireEvent("arrowclick", this, e);
29858                 if(this.arrowHandler){
29859                     this.arrowHandler.call(this.scope || this, this, e);
29860                 }
29861             }else{
29862                 this.fireEvent("click", this, e);
29863                 if(this.handler){
29864                     this.handler.call(this.scope || this, this, e);
29865                 }
29866             }
29867         }
29868     },
29869     // private
29870     onMouseDown : function(e){
29871         if(!this.disabled){
29872             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29873         }
29874     },
29875     // private
29876     onMouseUp : function(e){
29877         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29878     }   
29879 });
29880
29881
29882 // backwards compat
29883 Roo.MenuButton = Roo.SplitButton;/*
29884  * Based on:
29885  * Ext JS Library 1.1.1
29886  * Copyright(c) 2006-2007, Ext JS, LLC.
29887  *
29888  * Originally Released Under LGPL - original licence link has changed is not relivant.
29889  *
29890  * Fork - LGPL
29891  * <script type="text/javascript">
29892  */
29893
29894 /**
29895  * @class Roo.Toolbar
29896  * Basic Toolbar class.
29897  * @constructor
29898  * Creates a new Toolbar
29899  * @param {Object} container The config object
29900  */ 
29901 Roo.Toolbar = function(container, buttons, config)
29902 {
29903     /// old consturctor format still supported..
29904     if(container instanceof Array){ // omit the container for later rendering
29905         buttons = container;
29906         config = buttons;
29907         container = null;
29908     }
29909     if (typeof(container) == 'object' && container.xtype) {
29910         config = container;
29911         container = config.container;
29912         buttons = config.buttons || []; // not really - use items!!
29913     }
29914     var xitems = [];
29915     if (config && config.items) {
29916         xitems = config.items;
29917         delete config.items;
29918     }
29919     Roo.apply(this, config);
29920     this.buttons = buttons;
29921     
29922     if(container){
29923         this.render(container);
29924     }
29925     this.xitems = xitems;
29926     Roo.each(xitems, function(b) {
29927         this.add(b);
29928     }, this);
29929     
29930 };
29931
29932 Roo.Toolbar.prototype = {
29933     /**
29934      * @cfg {Array} items
29935      * array of button configs or elements to add (will be converted to a MixedCollection)
29936      */
29937     
29938     /**
29939      * @cfg {String/HTMLElement/Element} container
29940      * The id or element that will contain the toolbar
29941      */
29942     // private
29943     render : function(ct){
29944         this.el = Roo.get(ct);
29945         if(this.cls){
29946             this.el.addClass(this.cls);
29947         }
29948         // using a table allows for vertical alignment
29949         // 100% width is needed by Safari...
29950         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29951         this.tr = this.el.child("tr", true);
29952         var autoId = 0;
29953         this.items = new Roo.util.MixedCollection(false, function(o){
29954             return o.id || ("item" + (++autoId));
29955         });
29956         if(this.buttons){
29957             this.add.apply(this, this.buttons);
29958             delete this.buttons;
29959         }
29960     },
29961
29962     /**
29963      * Adds element(s) to the toolbar -- this function takes a variable number of 
29964      * arguments of mixed type and adds them to the toolbar.
29965      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29966      * <ul>
29967      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29968      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29969      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29970      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29971      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29972      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29973      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29974      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29975      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29976      * </ul>
29977      * @param {Mixed} arg2
29978      * @param {Mixed} etc.
29979      */
29980     add : function(){
29981         var a = arguments, l = a.length;
29982         for(var i = 0; i < l; i++){
29983             this._add(a[i]);
29984         }
29985     },
29986     // private..
29987     _add : function(el) {
29988         
29989         if (el.xtype) {
29990             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29991         }
29992         
29993         if (el.applyTo){ // some kind of form field
29994             return this.addField(el);
29995         } 
29996         if (el.render){ // some kind of Toolbar.Item
29997             return this.addItem(el);
29998         }
29999         if (typeof el == "string"){ // string
30000             if(el == "separator" || el == "-"){
30001                 return this.addSeparator();
30002             }
30003             if (el == " "){
30004                 return this.addSpacer();
30005             }
30006             if(el == "->"){
30007                 return this.addFill();
30008             }
30009             return this.addText(el);
30010             
30011         }
30012         if(el.tagName){ // element
30013             return this.addElement(el);
30014         }
30015         if(typeof el == "object"){ // must be button config?
30016             return this.addButton(el);
30017         }
30018         // and now what?!?!
30019         return false;
30020         
30021     },
30022     
30023     /**
30024      * Add an Xtype element
30025      * @param {Object} xtype Xtype Object
30026      * @return {Object} created Object
30027      */
30028     addxtype : function(e){
30029         return this.add(e);  
30030     },
30031     
30032     /**
30033      * Returns the Element for this toolbar.
30034      * @return {Roo.Element}
30035      */
30036     getEl : function(){
30037         return this.el;  
30038     },
30039     
30040     /**
30041      * Adds a separator
30042      * @return {Roo.Toolbar.Item} The separator item
30043      */
30044     addSeparator : function(){
30045         return this.addItem(new Roo.Toolbar.Separator());
30046     },
30047
30048     /**
30049      * Adds a spacer element
30050      * @return {Roo.Toolbar.Spacer} The spacer item
30051      */
30052     addSpacer : function(){
30053         return this.addItem(new Roo.Toolbar.Spacer());
30054     },
30055
30056     /**
30057      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30058      * @return {Roo.Toolbar.Fill} The fill item
30059      */
30060     addFill : function(){
30061         return this.addItem(new Roo.Toolbar.Fill());
30062     },
30063
30064     /**
30065      * Adds any standard HTML element to the toolbar
30066      * @param {String/HTMLElement/Element} el The element or id of the element to add
30067      * @return {Roo.Toolbar.Item} The element's item
30068      */
30069     addElement : function(el){
30070         return this.addItem(new Roo.Toolbar.Item(el));
30071     },
30072     /**
30073      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30074      * @type Roo.util.MixedCollection  
30075      */
30076     items : false,
30077      
30078     /**
30079      * Adds any Toolbar.Item or subclass
30080      * @param {Roo.Toolbar.Item} item
30081      * @return {Roo.Toolbar.Item} The item
30082      */
30083     addItem : function(item){
30084         var td = this.nextBlock();
30085         item.render(td);
30086         this.items.add(item);
30087         return item;
30088     },
30089     
30090     /**
30091      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30092      * @param {Object/Array} config A button config or array of configs
30093      * @return {Roo.Toolbar.Button/Array}
30094      */
30095     addButton : function(config){
30096         if(config instanceof Array){
30097             var buttons = [];
30098             for(var i = 0, len = config.length; i < len; i++) {
30099                 buttons.push(this.addButton(config[i]));
30100             }
30101             return buttons;
30102         }
30103         var b = config;
30104         if(!(config instanceof Roo.Toolbar.Button)){
30105             b = config.split ?
30106                 new Roo.Toolbar.SplitButton(config) :
30107                 new Roo.Toolbar.Button(config);
30108         }
30109         var td = this.nextBlock();
30110         b.render(td);
30111         this.items.add(b);
30112         return b;
30113     },
30114     
30115     /**
30116      * Adds text to the toolbar
30117      * @param {String} text The text to add
30118      * @return {Roo.Toolbar.Item} The element's item
30119      */
30120     addText : function(text){
30121         return this.addItem(new Roo.Toolbar.TextItem(text));
30122     },
30123     
30124     /**
30125      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30126      * @param {Number} index The index where the item is to be inserted
30127      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30128      * @return {Roo.Toolbar.Button/Item}
30129      */
30130     insertButton : function(index, item){
30131         if(item instanceof Array){
30132             var buttons = [];
30133             for(var i = 0, len = item.length; i < len; i++) {
30134                buttons.push(this.insertButton(index + i, item[i]));
30135             }
30136             return buttons;
30137         }
30138         if (!(item instanceof Roo.Toolbar.Button)){
30139            item = new Roo.Toolbar.Button(item);
30140         }
30141         var td = document.createElement("td");
30142         this.tr.insertBefore(td, this.tr.childNodes[index]);
30143         item.render(td);
30144         this.items.insert(index, item);
30145         return item;
30146     },
30147     
30148     /**
30149      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30150      * @param {Object} config
30151      * @return {Roo.Toolbar.Item} The element's item
30152      */
30153     addDom : function(config, returnEl){
30154         var td = this.nextBlock();
30155         Roo.DomHelper.overwrite(td, config);
30156         var ti = new Roo.Toolbar.Item(td.firstChild);
30157         ti.render(td);
30158         this.items.add(ti);
30159         return ti;
30160     },
30161
30162     /**
30163      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30164      * @type Roo.util.MixedCollection  
30165      */
30166     fields : false,
30167     
30168     /**
30169      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30170      * Note: the field should not have been rendered yet. For a field that has already been
30171      * rendered, use {@link #addElement}.
30172      * @param {Roo.form.Field} field
30173      * @return {Roo.ToolbarItem}
30174      */
30175      
30176       
30177     addField : function(field) {
30178         if (!this.fields) {
30179             var autoId = 0;
30180             this.fields = new Roo.util.MixedCollection(false, function(o){
30181                 return o.id || ("item" + (++autoId));
30182             });
30183
30184         }
30185         
30186         var td = this.nextBlock();
30187         field.render(td);
30188         var ti = new Roo.Toolbar.Item(td.firstChild);
30189         ti.render(td);
30190         this.items.add(ti);
30191         this.fields.add(field);
30192         return ti;
30193     },
30194     /**
30195      * Hide the toolbar
30196      * @method hide
30197      */
30198      
30199       
30200     hide : function()
30201     {
30202         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30203         this.el.child('div').hide();
30204     },
30205     /**
30206      * Show the toolbar
30207      * @method show
30208      */
30209     show : function()
30210     {
30211         this.el.child('div').show();
30212     },
30213       
30214     // private
30215     nextBlock : function(){
30216         var td = document.createElement("td");
30217         this.tr.appendChild(td);
30218         return td;
30219     },
30220
30221     // private
30222     destroy : function(){
30223         if(this.items){ // rendered?
30224             Roo.destroy.apply(Roo, this.items.items);
30225         }
30226         if(this.fields){ // rendered?
30227             Roo.destroy.apply(Roo, this.fields.items);
30228         }
30229         Roo.Element.uncache(this.el, this.tr);
30230     }
30231 };
30232
30233 /**
30234  * @class Roo.Toolbar.Item
30235  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30236  * @constructor
30237  * Creates a new Item
30238  * @param {HTMLElement} el 
30239  */
30240 Roo.Toolbar.Item = function(el){
30241     var cfg = {};
30242     if (typeof (el.xtype) != 'undefined') {
30243         cfg = el;
30244         el = cfg.el;
30245     }
30246     
30247     this.el = Roo.getDom(el);
30248     this.id = Roo.id(this.el);
30249     this.hidden = false;
30250     
30251     this.addEvents({
30252          /**
30253              * @event render
30254              * Fires when the button is rendered
30255              * @param {Button} this
30256              */
30257         'render': true
30258     });
30259     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30260 };
30261 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30262 //Roo.Toolbar.Item.prototype = {
30263     
30264     /**
30265      * Get this item's HTML Element
30266      * @return {HTMLElement}
30267      */
30268     getEl : function(){
30269        return this.el;  
30270     },
30271
30272     // private
30273     render : function(td){
30274         
30275          this.td = td;
30276         td.appendChild(this.el);
30277         
30278         this.fireEvent('render', this);
30279     },
30280     
30281     /**
30282      * Removes and destroys this item.
30283      */
30284     destroy : function(){
30285         this.td.parentNode.removeChild(this.td);
30286     },
30287     
30288     /**
30289      * Shows this item.
30290      */
30291     show: function(){
30292         this.hidden = false;
30293         this.td.style.display = "";
30294     },
30295     
30296     /**
30297      * Hides this item.
30298      */
30299     hide: function(){
30300         this.hidden = true;
30301         this.td.style.display = "none";
30302     },
30303     
30304     /**
30305      * Convenience function for boolean show/hide.
30306      * @param {Boolean} visible true to show/false to hide
30307      */
30308     setVisible: function(visible){
30309         if(visible) {
30310             this.show();
30311         }else{
30312             this.hide();
30313         }
30314     },
30315     
30316     /**
30317      * Try to focus this item.
30318      */
30319     focus : function(){
30320         Roo.fly(this.el).focus();
30321     },
30322     
30323     /**
30324      * Disables this item.
30325      */
30326     disable : function(){
30327         Roo.fly(this.td).addClass("x-item-disabled");
30328         this.disabled = true;
30329         this.el.disabled = true;
30330     },
30331     
30332     /**
30333      * Enables this item.
30334      */
30335     enable : function(){
30336         Roo.fly(this.td).removeClass("x-item-disabled");
30337         this.disabled = false;
30338         this.el.disabled = false;
30339     }
30340 });
30341
30342
30343 /**
30344  * @class Roo.Toolbar.Separator
30345  * @extends Roo.Toolbar.Item
30346  * A simple toolbar separator class
30347  * @constructor
30348  * Creates a new Separator
30349  */
30350 Roo.Toolbar.Separator = function(cfg){
30351     
30352     var s = document.createElement("span");
30353     s.className = "ytb-sep";
30354     if (cfg) {
30355         cfg.el = s;
30356     }
30357     
30358     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30359 };
30360 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30361     enable:Roo.emptyFn,
30362     disable:Roo.emptyFn,
30363     focus:Roo.emptyFn
30364 });
30365
30366 /**
30367  * @class Roo.Toolbar.Spacer
30368  * @extends Roo.Toolbar.Item
30369  * A simple element that adds extra horizontal space to a toolbar.
30370  * @constructor
30371  * Creates a new Spacer
30372  */
30373 Roo.Toolbar.Spacer = function(cfg){
30374     var s = document.createElement("div");
30375     s.className = "ytb-spacer";
30376     if (cfg) {
30377         cfg.el = s;
30378     }
30379     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30380 };
30381 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30382     enable:Roo.emptyFn,
30383     disable:Roo.emptyFn,
30384     focus:Roo.emptyFn
30385 });
30386
30387 /**
30388  * @class Roo.Toolbar.Fill
30389  * @extends Roo.Toolbar.Spacer
30390  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30391  * @constructor
30392  * Creates a new Spacer
30393  */
30394 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30395     // private
30396     render : function(td){
30397         td.style.width = '100%';
30398         Roo.Toolbar.Fill.superclass.render.call(this, td);
30399     }
30400 });
30401
30402 /**
30403  * @class Roo.Toolbar.TextItem
30404  * @extends Roo.Toolbar.Item
30405  * A simple class that renders text directly into a toolbar.
30406  * @constructor
30407  * Creates a new TextItem
30408  * @param {String} text
30409  */
30410 Roo.Toolbar.TextItem = function(cfg){
30411     var  text = cfg || "";
30412     if (typeof(cfg) == 'object') {
30413         text = cfg.text || "";
30414     }  else {
30415         cfg = null;
30416     }
30417     var s = document.createElement("span");
30418     s.className = "ytb-text";
30419     s.innerHTML = text;
30420     if (cfg) {
30421         cfg.el  = s;
30422     }
30423     
30424     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30425 };
30426 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30427     
30428      
30429     enable:Roo.emptyFn,
30430     disable:Roo.emptyFn,
30431     focus:Roo.emptyFn
30432 });
30433
30434 /**
30435  * @class Roo.Toolbar.Button
30436  * @extends Roo.Button
30437  * A button that renders into a toolbar.
30438  * @constructor
30439  * Creates a new Button
30440  * @param {Object} config A standard {@link Roo.Button} config object
30441  */
30442 Roo.Toolbar.Button = function(config){
30443     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30444 };
30445 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30446     render : function(td){
30447         this.td = td;
30448         Roo.Toolbar.Button.superclass.render.call(this, td);
30449     },
30450     
30451     /**
30452      * Removes and destroys this button
30453      */
30454     destroy : function(){
30455         Roo.Toolbar.Button.superclass.destroy.call(this);
30456         this.td.parentNode.removeChild(this.td);
30457     },
30458     
30459     /**
30460      * Shows this button
30461      */
30462     show: function(){
30463         this.hidden = false;
30464         this.td.style.display = "";
30465     },
30466     
30467     /**
30468      * Hides this button
30469      */
30470     hide: function(){
30471         this.hidden = true;
30472         this.td.style.display = "none";
30473     },
30474
30475     /**
30476      * Disables this item
30477      */
30478     disable : function(){
30479         Roo.fly(this.td).addClass("x-item-disabled");
30480         this.disabled = true;
30481     },
30482
30483     /**
30484      * Enables this item
30485      */
30486     enable : function(){
30487         Roo.fly(this.td).removeClass("x-item-disabled");
30488         this.disabled = false;
30489     }
30490 });
30491 // backwards compat
30492 Roo.ToolbarButton = Roo.Toolbar.Button;
30493
30494 /**
30495  * @class Roo.Toolbar.SplitButton
30496  * @extends Roo.SplitButton
30497  * A menu button that renders into a toolbar.
30498  * @constructor
30499  * Creates a new SplitButton
30500  * @param {Object} config A standard {@link Roo.SplitButton} config object
30501  */
30502 Roo.Toolbar.SplitButton = function(config){
30503     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30504 };
30505 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30506     render : function(td){
30507         this.td = td;
30508         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30509     },
30510     
30511     /**
30512      * Removes and destroys this button
30513      */
30514     destroy : function(){
30515         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30516         this.td.parentNode.removeChild(this.td);
30517     },
30518     
30519     /**
30520      * Shows this button
30521      */
30522     show: function(){
30523         this.hidden = false;
30524         this.td.style.display = "";
30525     },
30526     
30527     /**
30528      * Hides this button
30529      */
30530     hide: function(){
30531         this.hidden = true;
30532         this.td.style.display = "none";
30533     }
30534 });
30535
30536 // backwards compat
30537 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30538  * Based on:
30539  * Ext JS Library 1.1.1
30540  * Copyright(c) 2006-2007, Ext JS, LLC.
30541  *
30542  * Originally Released Under LGPL - original licence link has changed is not relivant.
30543  *
30544  * Fork - LGPL
30545  * <script type="text/javascript">
30546  */
30547  
30548 /**
30549  * @class Roo.PagingToolbar
30550  * @extends Roo.Toolbar
30551  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30552  * @constructor
30553  * Create a new PagingToolbar
30554  * @param {Object} config The config object
30555  */
30556 Roo.PagingToolbar = function(el, ds, config)
30557 {
30558     // old args format still supported... - xtype is prefered..
30559     if (typeof(el) == 'object' && el.xtype) {
30560         // created from xtype...
30561         config = el;
30562         ds = el.dataSource;
30563         el = config.container;
30564     }
30565     var items = [];
30566     if (config.items) {
30567         items = config.items;
30568         config.items = [];
30569     }
30570     
30571     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30572     this.ds = ds;
30573     this.cursor = 0;
30574     this.renderButtons(this.el);
30575     this.bind(ds);
30576     
30577     // supprot items array.
30578    
30579     Roo.each(items, function(e) {
30580         this.add(Roo.factory(e));
30581     },this);
30582     
30583 };
30584
30585 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30586     /**
30587      * @cfg {Roo.data.Store} dataSource
30588      * The underlying data store providing the paged data
30589      */
30590     /**
30591      * @cfg {String/HTMLElement/Element} container
30592      * container The id or element that will contain the toolbar
30593      */
30594     /**
30595      * @cfg {Boolean} displayInfo
30596      * True to display the displayMsg (defaults to false)
30597      */
30598     /**
30599      * @cfg {Number} pageSize
30600      * The number of records to display per page (defaults to 20)
30601      */
30602     pageSize: 20,
30603     /**
30604      * @cfg {String} displayMsg
30605      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30606      */
30607     displayMsg : 'Displaying {0} - {1} of {2}',
30608     /**
30609      * @cfg {String} emptyMsg
30610      * The message to display when no records are found (defaults to "No data to display")
30611      */
30612     emptyMsg : 'No data to display',
30613     /**
30614      * Customizable piece of the default paging text (defaults to "Page")
30615      * @type String
30616      */
30617     beforePageText : "Page",
30618     /**
30619      * Customizable piece of the default paging text (defaults to "of %0")
30620      * @type String
30621      */
30622     afterPageText : "of {0}",
30623     /**
30624      * Customizable piece of the default paging text (defaults to "First Page")
30625      * @type String
30626      */
30627     firstText : "First Page",
30628     /**
30629      * Customizable piece of the default paging text (defaults to "Previous Page")
30630      * @type String
30631      */
30632     prevText : "Previous Page",
30633     /**
30634      * Customizable piece of the default paging text (defaults to "Next Page")
30635      * @type String
30636      */
30637     nextText : "Next Page",
30638     /**
30639      * Customizable piece of the default paging text (defaults to "Last Page")
30640      * @type String
30641      */
30642     lastText : "Last Page",
30643     /**
30644      * Customizable piece of the default paging text (defaults to "Refresh")
30645      * @type String
30646      */
30647     refreshText : "Refresh",
30648
30649     // private
30650     renderButtons : function(el){
30651         Roo.PagingToolbar.superclass.render.call(this, el);
30652         this.first = this.addButton({
30653             tooltip: this.firstText,
30654             cls: "x-btn-icon x-grid-page-first",
30655             disabled: true,
30656             handler: this.onClick.createDelegate(this, ["first"])
30657         });
30658         this.prev = this.addButton({
30659             tooltip: this.prevText,
30660             cls: "x-btn-icon x-grid-page-prev",
30661             disabled: true,
30662             handler: this.onClick.createDelegate(this, ["prev"])
30663         });
30664         //this.addSeparator();
30665         this.add(this.beforePageText);
30666         this.field = Roo.get(this.addDom({
30667            tag: "input",
30668            type: "text",
30669            size: "3",
30670            value: "1",
30671            cls: "x-grid-page-number"
30672         }).el);
30673         this.field.on("keydown", this.onPagingKeydown, this);
30674         this.field.on("focus", function(){this.dom.select();});
30675         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30676         this.field.setHeight(18);
30677         //this.addSeparator();
30678         this.next = this.addButton({
30679             tooltip: this.nextText,
30680             cls: "x-btn-icon x-grid-page-next",
30681             disabled: true,
30682             handler: this.onClick.createDelegate(this, ["next"])
30683         });
30684         this.last = this.addButton({
30685             tooltip: this.lastText,
30686             cls: "x-btn-icon x-grid-page-last",
30687             disabled: true,
30688             handler: this.onClick.createDelegate(this, ["last"])
30689         });
30690         //this.addSeparator();
30691         this.loading = this.addButton({
30692             tooltip: this.refreshText,
30693             cls: "x-btn-icon x-grid-loading",
30694             handler: this.onClick.createDelegate(this, ["refresh"])
30695         });
30696
30697         if(this.displayInfo){
30698             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30699         }
30700     },
30701
30702     // private
30703     updateInfo : function(){
30704         if(this.displayEl){
30705             var count = this.ds.getCount();
30706             var msg = count == 0 ?
30707                 this.emptyMsg :
30708                 String.format(
30709                     this.displayMsg,
30710                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30711                 );
30712             this.displayEl.update(msg);
30713         }
30714     },
30715
30716     // private
30717     onLoad : function(ds, r, o){
30718        this.cursor = o.params ? o.params.start : 0;
30719        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30720
30721        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30722        this.field.dom.value = ap;
30723        this.first.setDisabled(ap == 1);
30724        this.prev.setDisabled(ap == 1);
30725        this.next.setDisabled(ap == ps);
30726        this.last.setDisabled(ap == ps);
30727        this.loading.enable();
30728        this.updateInfo();
30729     },
30730
30731     // private
30732     getPageData : function(){
30733         var total = this.ds.getTotalCount();
30734         return {
30735             total : total,
30736             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30737             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30738         };
30739     },
30740
30741     // private
30742     onLoadError : function(){
30743         this.loading.enable();
30744     },
30745
30746     // private
30747     onPagingKeydown : function(e){
30748         var k = e.getKey();
30749         var d = this.getPageData();
30750         if(k == e.RETURN){
30751             var v = this.field.dom.value, pageNum;
30752             if(!v || isNaN(pageNum = parseInt(v, 10))){
30753                 this.field.dom.value = d.activePage;
30754                 return;
30755             }
30756             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30757             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30758             e.stopEvent();
30759         }
30760         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))
30761         {
30762           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30763           this.field.dom.value = pageNum;
30764           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30765           e.stopEvent();
30766         }
30767         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30768         {
30769           var v = this.field.dom.value, pageNum; 
30770           var increment = (e.shiftKey) ? 10 : 1;
30771           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30772             increment *= -1;
30773           }
30774           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30775             this.field.dom.value = d.activePage;
30776             return;
30777           }
30778           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30779           {
30780             this.field.dom.value = parseInt(v, 10) + increment;
30781             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30782             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30783           }
30784           e.stopEvent();
30785         }
30786     },
30787
30788     // private
30789     beforeLoad : function(){
30790         if(this.loading){
30791             this.loading.disable();
30792         }
30793     },
30794
30795     // private
30796     onClick : function(which){
30797         var ds = this.ds;
30798         switch(which){
30799             case "first":
30800                 ds.load({params:{start: 0, limit: this.pageSize}});
30801             break;
30802             case "prev":
30803                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30804             break;
30805             case "next":
30806                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30807             break;
30808             case "last":
30809                 var total = ds.getTotalCount();
30810                 var extra = total % this.pageSize;
30811                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30812                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30813             break;
30814             case "refresh":
30815                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30816             break;
30817         }
30818     },
30819
30820     /**
30821      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30822      * @param {Roo.data.Store} store The data store to unbind
30823      */
30824     unbind : function(ds){
30825         ds.un("beforeload", this.beforeLoad, this);
30826         ds.un("load", this.onLoad, this);
30827         ds.un("loadexception", this.onLoadError, this);
30828         ds.un("remove", this.updateInfo, this);
30829         ds.un("add", this.updateInfo, this);
30830         this.ds = undefined;
30831     },
30832
30833     /**
30834      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30835      * @param {Roo.data.Store} store The data store to bind
30836      */
30837     bind : function(ds){
30838         ds.on("beforeload", this.beforeLoad, this);
30839         ds.on("load", this.onLoad, this);
30840         ds.on("loadexception", this.onLoadError, this);
30841         ds.on("remove", this.updateInfo, this);
30842         ds.on("add", this.updateInfo, this);
30843         this.ds = ds;
30844     }
30845 });/*
30846  * Based on:
30847  * Ext JS Library 1.1.1
30848  * Copyright(c) 2006-2007, Ext JS, LLC.
30849  *
30850  * Originally Released Under LGPL - original licence link has changed is not relivant.
30851  *
30852  * Fork - LGPL
30853  * <script type="text/javascript">
30854  */
30855
30856 /**
30857  * @class Roo.Resizable
30858  * @extends Roo.util.Observable
30859  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30860  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30861  * 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
30862  * the element will be wrapped for you automatically.</p>
30863  * <p>Here is the list of valid resize handles:</p>
30864  * <pre>
30865 Value   Description
30866 ------  -------------------
30867  'n'     north
30868  's'     south
30869  'e'     east
30870  'w'     west
30871  'nw'    northwest
30872  'sw'    southwest
30873  'se'    southeast
30874  'ne'    northeast
30875  'hd'    horizontal drag
30876  'all'   all
30877 </pre>
30878  * <p>Here's an example showing the creation of a typical Resizable:</p>
30879  * <pre><code>
30880 var resizer = new Roo.Resizable("element-id", {
30881     handles: 'all',
30882     minWidth: 200,
30883     minHeight: 100,
30884     maxWidth: 500,
30885     maxHeight: 400,
30886     pinned: true
30887 });
30888 resizer.on("resize", myHandler);
30889 </code></pre>
30890  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30891  * resizer.east.setDisplayed(false);</p>
30892  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30893  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30894  * resize operation's new size (defaults to [0, 0])
30895  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30896  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30897  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30898  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30899  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30900  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30901  * @cfg {Number} width The width of the element in pixels (defaults to null)
30902  * @cfg {Number} height The height of the element in pixels (defaults to null)
30903  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30904  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30905  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30906  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30907  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30908  * in favor of the handles config option (defaults to false)
30909  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30910  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30911  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30912  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30913  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30914  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30915  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30916  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30917  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30918  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30919  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30920  * @constructor
30921  * Create a new resizable component
30922  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30923  * @param {Object} config configuration options
30924   */
30925 Roo.Resizable = function(el, config)
30926 {
30927     this.el = Roo.get(el);
30928
30929     if(config && config.wrap){
30930         config.resizeChild = this.el;
30931         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30932         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30933         this.el.setStyle("overflow", "hidden");
30934         this.el.setPositioning(config.resizeChild.getPositioning());
30935         config.resizeChild.clearPositioning();
30936         if(!config.width || !config.height){
30937             var csize = config.resizeChild.getSize();
30938             this.el.setSize(csize.width, csize.height);
30939         }
30940         if(config.pinned && !config.adjustments){
30941             config.adjustments = "auto";
30942         }
30943     }
30944
30945     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30946     this.proxy.unselectable();
30947     this.proxy.enableDisplayMode('block');
30948
30949     Roo.apply(this, config);
30950
30951     if(this.pinned){
30952         this.disableTrackOver = true;
30953         this.el.addClass("x-resizable-pinned");
30954     }
30955     // if the element isn't positioned, make it relative
30956     var position = this.el.getStyle("position");
30957     if(position != "absolute" && position != "fixed"){
30958         this.el.setStyle("position", "relative");
30959     }
30960     if(!this.handles){ // no handles passed, must be legacy style
30961         this.handles = 's,e,se';
30962         if(this.multiDirectional){
30963             this.handles += ',n,w';
30964         }
30965     }
30966     if(this.handles == "all"){
30967         this.handles = "n s e w ne nw se sw";
30968     }
30969     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30970     var ps = Roo.Resizable.positions;
30971     for(var i = 0, len = hs.length; i < len; i++){
30972         if(hs[i] && ps[hs[i]]){
30973             var pos = ps[hs[i]];
30974             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30975         }
30976     }
30977     // legacy
30978     this.corner = this.southeast;
30979     
30980     // updateBox = the box can move..
30981     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30982         this.updateBox = true;
30983     }
30984
30985     this.activeHandle = null;
30986
30987     if(this.resizeChild){
30988         if(typeof this.resizeChild == "boolean"){
30989             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30990         }else{
30991             this.resizeChild = Roo.get(this.resizeChild, true);
30992         }
30993     }
30994     
30995     if(this.adjustments == "auto"){
30996         var rc = this.resizeChild;
30997         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30998         if(rc && (hw || hn)){
30999             rc.position("relative");
31000             rc.setLeft(hw ? hw.el.getWidth() : 0);
31001             rc.setTop(hn ? hn.el.getHeight() : 0);
31002         }
31003         this.adjustments = [
31004             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31005             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31006         ];
31007     }
31008
31009     if(this.draggable){
31010         this.dd = this.dynamic ?
31011             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31012         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31013     }
31014
31015     // public events
31016     this.addEvents({
31017         /**
31018          * @event beforeresize
31019          * Fired before resize is allowed. Set enabled to false to cancel resize.
31020          * @param {Roo.Resizable} this
31021          * @param {Roo.EventObject} e The mousedown event
31022          */
31023         "beforeresize" : true,
31024         /**
31025          * @event resizing
31026          * Fired a resizing.
31027          * @param {Roo.Resizable} this
31028          * @param {Number} x The new x position
31029          * @param {Number} y The new y position
31030          * @param {Number} w The new w width
31031          * @param {Number} h The new h hight
31032          * @param {Roo.EventObject} e The mouseup event
31033          */
31034         "resizing" : true,
31035         /**
31036          * @event resize
31037          * Fired after a resize.
31038          * @param {Roo.Resizable} this
31039          * @param {Number} width The new width
31040          * @param {Number} height The new height
31041          * @param {Roo.EventObject} e The mouseup event
31042          */
31043         "resize" : true
31044     });
31045
31046     if(this.width !== null && this.height !== null){
31047         this.resizeTo(this.width, this.height);
31048     }else{
31049         this.updateChildSize();
31050     }
31051     if(Roo.isIE){
31052         this.el.dom.style.zoom = 1;
31053     }
31054     Roo.Resizable.superclass.constructor.call(this);
31055 };
31056
31057 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31058         resizeChild : false,
31059         adjustments : [0, 0],
31060         minWidth : 5,
31061         minHeight : 5,
31062         maxWidth : 10000,
31063         maxHeight : 10000,
31064         enabled : true,
31065         animate : false,
31066         duration : .35,
31067         dynamic : false,
31068         handles : false,
31069         multiDirectional : false,
31070         disableTrackOver : false,
31071         easing : 'easeOutStrong',
31072         widthIncrement : 0,
31073         heightIncrement : 0,
31074         pinned : false,
31075         width : null,
31076         height : null,
31077         preserveRatio : false,
31078         transparent: false,
31079         minX: 0,
31080         minY: 0,
31081         draggable: false,
31082
31083         /**
31084          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31085          */
31086         constrainTo: undefined,
31087         /**
31088          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31089          */
31090         resizeRegion: undefined,
31091
31092
31093     /**
31094      * Perform a manual resize
31095      * @param {Number} width
31096      * @param {Number} height
31097      */
31098     resizeTo : function(width, height){
31099         this.el.setSize(width, height);
31100         this.updateChildSize();
31101         this.fireEvent("resize", this, width, height, null);
31102     },
31103
31104     // private
31105     startSizing : function(e, handle){
31106         this.fireEvent("beforeresize", this, e);
31107         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31108
31109             if(!this.overlay){
31110                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31111                 this.overlay.unselectable();
31112                 this.overlay.enableDisplayMode("block");
31113                 this.overlay.on("mousemove", this.onMouseMove, this);
31114                 this.overlay.on("mouseup", this.onMouseUp, this);
31115             }
31116             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31117
31118             this.resizing = true;
31119             this.startBox = this.el.getBox();
31120             this.startPoint = e.getXY();
31121             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31122                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31123
31124             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31125             this.overlay.show();
31126
31127             if(this.constrainTo) {
31128                 var ct = Roo.get(this.constrainTo);
31129                 this.resizeRegion = ct.getRegion().adjust(
31130                     ct.getFrameWidth('t'),
31131                     ct.getFrameWidth('l'),
31132                     -ct.getFrameWidth('b'),
31133                     -ct.getFrameWidth('r')
31134                 );
31135             }
31136
31137             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31138             this.proxy.show();
31139             this.proxy.setBox(this.startBox);
31140             if(!this.dynamic){
31141                 this.proxy.setStyle('visibility', 'visible');
31142             }
31143         }
31144     },
31145
31146     // private
31147     onMouseDown : function(handle, e){
31148         if(this.enabled){
31149             e.stopEvent();
31150             this.activeHandle = handle;
31151             this.startSizing(e, handle);
31152         }
31153     },
31154
31155     // private
31156     onMouseUp : function(e){
31157         var size = this.resizeElement();
31158         this.resizing = false;
31159         this.handleOut();
31160         this.overlay.hide();
31161         this.proxy.hide();
31162         this.fireEvent("resize", this, size.width, size.height, e);
31163     },
31164
31165     // private
31166     updateChildSize : function(){
31167         
31168         if(this.resizeChild){
31169             var el = this.el;
31170             var child = this.resizeChild;
31171             var adj = this.adjustments;
31172             if(el.dom.offsetWidth){
31173                 var b = el.getSize(true);
31174                 child.setSize(b.width+adj[0], b.height+adj[1]);
31175             }
31176             // Second call here for IE
31177             // The first call enables instant resizing and
31178             // the second call corrects scroll bars if they
31179             // exist
31180             if(Roo.isIE){
31181                 setTimeout(function(){
31182                     if(el.dom.offsetWidth){
31183                         var b = el.getSize(true);
31184                         child.setSize(b.width+adj[0], b.height+adj[1]);
31185                     }
31186                 }, 10);
31187             }
31188         }
31189     },
31190
31191     // private
31192     snap : function(value, inc, min){
31193         if(!inc || !value) {
31194             return value;
31195         }
31196         var newValue = value;
31197         var m = value % inc;
31198         if(m > 0){
31199             if(m > (inc/2)){
31200                 newValue = value + (inc-m);
31201             }else{
31202                 newValue = value - m;
31203             }
31204         }
31205         return Math.max(min, newValue);
31206     },
31207
31208     // private
31209     resizeElement : function(){
31210         var box = this.proxy.getBox();
31211         if(this.updateBox){
31212             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31213         }else{
31214             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31215         }
31216         this.updateChildSize();
31217         if(!this.dynamic){
31218             this.proxy.hide();
31219         }
31220         return box;
31221     },
31222
31223     // private
31224     constrain : function(v, diff, m, mx){
31225         if(v - diff < m){
31226             diff = v - m;
31227         }else if(v - diff > mx){
31228             diff = mx - v;
31229         }
31230         return diff;
31231     },
31232
31233     // private
31234     onMouseMove : function(e){
31235         
31236         if(this.enabled){
31237             try{// try catch so if something goes wrong the user doesn't get hung
31238
31239             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31240                 return;
31241             }
31242
31243             //var curXY = this.startPoint;
31244             var curSize = this.curSize || this.startBox;
31245             var x = this.startBox.x, y = this.startBox.y;
31246             var ox = x, oy = y;
31247             var w = curSize.width, h = curSize.height;
31248             var ow = w, oh = h;
31249             var mw = this.minWidth, mh = this.minHeight;
31250             var mxw = this.maxWidth, mxh = this.maxHeight;
31251             var wi = this.widthIncrement;
31252             var hi = this.heightIncrement;
31253
31254             var eventXY = e.getXY();
31255             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31256             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31257
31258             var pos = this.activeHandle.position;
31259
31260             switch(pos){
31261                 case "east":
31262                     w += diffX;
31263                     w = Math.min(Math.max(mw, w), mxw);
31264                     break;
31265              
31266                 case "south":
31267                     h += diffY;
31268                     h = Math.min(Math.max(mh, h), mxh);
31269                     break;
31270                 case "southeast":
31271                     w += diffX;
31272                     h += diffY;
31273                     w = Math.min(Math.max(mw, w), mxw);
31274                     h = Math.min(Math.max(mh, h), mxh);
31275                     break;
31276                 case "north":
31277                     diffY = this.constrain(h, diffY, mh, mxh);
31278                     y += diffY;
31279                     h -= diffY;
31280                     break;
31281                 case "hdrag":
31282                     
31283                     if (wi) {
31284                         var adiffX = Math.abs(diffX);
31285                         var sub = (adiffX % wi); // how much 
31286                         if (sub > (wi/2)) { // far enough to snap
31287                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31288                         } else {
31289                             // remove difference.. 
31290                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31291                         }
31292                     }
31293                     x += diffX;
31294                     x = Math.max(this.minX, x);
31295                     break;
31296                 case "west":
31297                     diffX = this.constrain(w, diffX, mw, mxw);
31298                     x += diffX;
31299                     w -= diffX;
31300                     break;
31301                 case "northeast":
31302                     w += diffX;
31303                     w = Math.min(Math.max(mw, w), mxw);
31304                     diffY = this.constrain(h, diffY, mh, mxh);
31305                     y += diffY;
31306                     h -= diffY;
31307                     break;
31308                 case "northwest":
31309                     diffX = this.constrain(w, diffX, mw, mxw);
31310                     diffY = this.constrain(h, diffY, mh, mxh);
31311                     y += diffY;
31312                     h -= diffY;
31313                     x += diffX;
31314                     w -= diffX;
31315                     break;
31316                case "southwest":
31317                     diffX = this.constrain(w, diffX, mw, mxw);
31318                     h += diffY;
31319                     h = Math.min(Math.max(mh, h), mxh);
31320                     x += diffX;
31321                     w -= diffX;
31322                     break;
31323             }
31324
31325             var sw = this.snap(w, wi, mw);
31326             var sh = this.snap(h, hi, mh);
31327             if(sw != w || sh != h){
31328                 switch(pos){
31329                     case "northeast":
31330                         y -= sh - h;
31331                     break;
31332                     case "north":
31333                         y -= sh - h;
31334                         break;
31335                     case "southwest":
31336                         x -= sw - w;
31337                     break;
31338                     case "west":
31339                         x -= sw - w;
31340                         break;
31341                     case "northwest":
31342                         x -= sw - w;
31343                         y -= sh - h;
31344                     break;
31345                 }
31346                 w = sw;
31347                 h = sh;
31348             }
31349
31350             if(this.preserveRatio){
31351                 switch(pos){
31352                     case "southeast":
31353                     case "east":
31354                         h = oh * (w/ow);
31355                         h = Math.min(Math.max(mh, h), mxh);
31356                         w = ow * (h/oh);
31357                        break;
31358                     case "south":
31359                         w = ow * (h/oh);
31360                         w = Math.min(Math.max(mw, w), mxw);
31361                         h = oh * (w/ow);
31362                         break;
31363                     case "northeast":
31364                         w = ow * (h/oh);
31365                         w = Math.min(Math.max(mw, w), mxw);
31366                         h = oh * (w/ow);
31367                     break;
31368                     case "north":
31369                         var tw = w;
31370                         w = ow * (h/oh);
31371                         w = Math.min(Math.max(mw, w), mxw);
31372                         h = oh * (w/ow);
31373                         x += (tw - w) / 2;
31374                         break;
31375                     case "southwest":
31376                         h = oh * (w/ow);
31377                         h = Math.min(Math.max(mh, h), mxh);
31378                         var tw = w;
31379                         w = ow * (h/oh);
31380                         x += tw - w;
31381                         break;
31382                     case "west":
31383                         var th = h;
31384                         h = oh * (w/ow);
31385                         h = Math.min(Math.max(mh, h), mxh);
31386                         y += (th - h) / 2;
31387                         var tw = w;
31388                         w = ow * (h/oh);
31389                         x += tw - w;
31390                        break;
31391                     case "northwest":
31392                         var tw = w;
31393                         var th = h;
31394                         h = oh * (w/ow);
31395                         h = Math.min(Math.max(mh, h), mxh);
31396                         w = ow * (h/oh);
31397                         y += th - h;
31398                         x += tw - w;
31399                        break;
31400
31401                 }
31402             }
31403             if (pos == 'hdrag') {
31404                 w = ow;
31405             }
31406             this.proxy.setBounds(x, y, w, h);
31407             if(this.dynamic){
31408                 this.resizeElement();
31409             }
31410             }catch(e){}
31411         }
31412         this.fireEvent("resizing", this, x, y, w, h, e);
31413     },
31414
31415     // private
31416     handleOver : function(){
31417         if(this.enabled){
31418             this.el.addClass("x-resizable-over");
31419         }
31420     },
31421
31422     // private
31423     handleOut : function(){
31424         if(!this.resizing){
31425             this.el.removeClass("x-resizable-over");
31426         }
31427     },
31428
31429     /**
31430      * Returns the element this component is bound to.
31431      * @return {Roo.Element}
31432      */
31433     getEl : function(){
31434         return this.el;
31435     },
31436
31437     /**
31438      * Returns the resizeChild element (or null).
31439      * @return {Roo.Element}
31440      */
31441     getResizeChild : function(){
31442         return this.resizeChild;
31443     },
31444     groupHandler : function()
31445     {
31446         
31447     },
31448     /**
31449      * Destroys this resizable. If the element was wrapped and
31450      * removeEl is not true then the element remains.
31451      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31452      */
31453     destroy : function(removeEl){
31454         this.proxy.remove();
31455         if(this.overlay){
31456             this.overlay.removeAllListeners();
31457             this.overlay.remove();
31458         }
31459         var ps = Roo.Resizable.positions;
31460         for(var k in ps){
31461             if(typeof ps[k] != "function" && this[ps[k]]){
31462                 var h = this[ps[k]];
31463                 h.el.removeAllListeners();
31464                 h.el.remove();
31465             }
31466         }
31467         if(removeEl){
31468             this.el.update("");
31469             this.el.remove();
31470         }
31471     }
31472 });
31473
31474 // private
31475 // hash to map config positions to true positions
31476 Roo.Resizable.positions = {
31477     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31478     hd: "hdrag"
31479 };
31480
31481 // private
31482 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31483     if(!this.tpl){
31484         // only initialize the template if resizable is used
31485         var tpl = Roo.DomHelper.createTemplate(
31486             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31487         );
31488         tpl.compile();
31489         Roo.Resizable.Handle.prototype.tpl = tpl;
31490     }
31491     this.position = pos;
31492     this.rz = rz;
31493     // show north drag fro topdra
31494     var handlepos = pos == 'hdrag' ? 'north' : pos;
31495     
31496     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31497     if (pos == 'hdrag') {
31498         this.el.setStyle('cursor', 'pointer');
31499     }
31500     this.el.unselectable();
31501     if(transparent){
31502         this.el.setOpacity(0);
31503     }
31504     this.el.on("mousedown", this.onMouseDown, this);
31505     if(!disableTrackOver){
31506         this.el.on("mouseover", this.onMouseOver, this);
31507         this.el.on("mouseout", this.onMouseOut, this);
31508     }
31509 };
31510
31511 // private
31512 Roo.Resizable.Handle.prototype = {
31513     afterResize : function(rz){
31514         Roo.log('after?');
31515         // do nothing
31516     },
31517     // private
31518     onMouseDown : function(e){
31519         this.rz.onMouseDown(this, e);
31520     },
31521     // private
31522     onMouseOver : function(e){
31523         this.rz.handleOver(this, e);
31524     },
31525     // private
31526     onMouseOut : function(e){
31527         this.rz.handleOut(this, e);
31528     }
31529 };/*
31530  * Based on:
31531  * Ext JS Library 1.1.1
31532  * Copyright(c) 2006-2007, Ext JS, LLC.
31533  *
31534  * Originally Released Under LGPL - original licence link has changed is not relivant.
31535  *
31536  * Fork - LGPL
31537  * <script type="text/javascript">
31538  */
31539
31540 /**
31541  * @class Roo.Editor
31542  * @extends Roo.Component
31543  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31544  * @constructor
31545  * Create a new Editor
31546  * @param {Roo.form.Field} field The Field object (or descendant)
31547  * @param {Object} config The config object
31548  */
31549 Roo.Editor = function(field, config){
31550     Roo.Editor.superclass.constructor.call(this, config);
31551     this.field = field;
31552     this.addEvents({
31553         /**
31554              * @event beforestartedit
31555              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31556              * false from the handler of this event.
31557              * @param {Editor} this
31558              * @param {Roo.Element} boundEl The underlying element bound to this editor
31559              * @param {Mixed} value The field value being set
31560              */
31561         "beforestartedit" : true,
31562         /**
31563              * @event startedit
31564              * Fires when this editor is displayed
31565              * @param {Roo.Element} boundEl The underlying element bound to this editor
31566              * @param {Mixed} value The starting field value
31567              */
31568         "startedit" : true,
31569         /**
31570              * @event beforecomplete
31571              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31572              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31573              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31574              * event will not fire since no edit actually occurred.
31575              * @param {Editor} this
31576              * @param {Mixed} value The current field value
31577              * @param {Mixed} startValue The original field value
31578              */
31579         "beforecomplete" : true,
31580         /**
31581              * @event complete
31582              * Fires after editing is complete and any changed value has been written to the underlying field.
31583              * @param {Editor} this
31584              * @param {Mixed} value The current field value
31585              * @param {Mixed} startValue The original field value
31586              */
31587         "complete" : true,
31588         /**
31589          * @event specialkey
31590          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31591          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31592          * @param {Roo.form.Field} this
31593          * @param {Roo.EventObject} e The event object
31594          */
31595         "specialkey" : true
31596     });
31597 };
31598
31599 Roo.extend(Roo.Editor, Roo.Component, {
31600     /**
31601      * @cfg {Boolean/String} autosize
31602      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31603      * or "height" to adopt the height only (defaults to false)
31604      */
31605     /**
31606      * @cfg {Boolean} revertInvalid
31607      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31608      * validation fails (defaults to true)
31609      */
31610     /**
31611      * @cfg {Boolean} ignoreNoChange
31612      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31613      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31614      * will never be ignored.
31615      */
31616     /**
31617      * @cfg {Boolean} hideEl
31618      * False to keep the bound element visible while the editor is displayed (defaults to true)
31619      */
31620     /**
31621      * @cfg {Mixed} value
31622      * The data value of the underlying field (defaults to "")
31623      */
31624     value : "",
31625     /**
31626      * @cfg {String} alignment
31627      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31628      */
31629     alignment: "c-c?",
31630     /**
31631      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31632      * for bottom-right shadow (defaults to "frame")
31633      */
31634     shadow : "frame",
31635     /**
31636      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31637      */
31638     constrain : false,
31639     /**
31640      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31641      */
31642     completeOnEnter : false,
31643     /**
31644      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31645      */
31646     cancelOnEsc : false,
31647     /**
31648      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31649      */
31650     updateEl : false,
31651
31652     // private
31653     onRender : function(ct, position){
31654         this.el = new Roo.Layer({
31655             shadow: this.shadow,
31656             cls: "x-editor",
31657             parentEl : ct,
31658             shim : this.shim,
31659             shadowOffset:4,
31660             id: this.id,
31661             constrain: this.constrain
31662         });
31663         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31664         if(this.field.msgTarget != 'title'){
31665             this.field.msgTarget = 'qtip';
31666         }
31667         this.field.render(this.el);
31668         if(Roo.isGecko){
31669             this.field.el.dom.setAttribute('autocomplete', 'off');
31670         }
31671         this.field.on("specialkey", this.onSpecialKey, this);
31672         if(this.swallowKeys){
31673             this.field.el.swallowEvent(['keydown','keypress']);
31674         }
31675         this.field.show();
31676         this.field.on("blur", this.onBlur, this);
31677         if(this.field.grow){
31678             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31679         }
31680     },
31681
31682     onSpecialKey : function(field, e)
31683     {
31684         //Roo.log('editor onSpecialKey');
31685         if(this.completeOnEnter && e.getKey() == e.ENTER){
31686             e.stopEvent();
31687             this.completeEdit();
31688             return;
31689         }
31690         // do not fire special key otherwise it might hide close the editor...
31691         if(e.getKey() == e.ENTER){    
31692             return;
31693         }
31694         if(this.cancelOnEsc && e.getKey() == e.ESC){
31695             this.cancelEdit();
31696             return;
31697         } 
31698         this.fireEvent('specialkey', field, e);
31699     
31700     },
31701
31702     /**
31703      * Starts the editing process and shows the editor.
31704      * @param {String/HTMLElement/Element} el The element to edit
31705      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31706       * to the innerHTML of el.
31707      */
31708     startEdit : function(el, value){
31709         if(this.editing){
31710             this.completeEdit();
31711         }
31712         this.boundEl = Roo.get(el);
31713         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31714         if(!this.rendered){
31715             this.render(this.parentEl || document.body);
31716         }
31717         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31718             return;
31719         }
31720         this.startValue = v;
31721         this.field.setValue(v);
31722         if(this.autoSize){
31723             var sz = this.boundEl.getSize();
31724             switch(this.autoSize){
31725                 case "width":
31726                 this.setSize(sz.width,  "");
31727                 break;
31728                 case "height":
31729                 this.setSize("",  sz.height);
31730                 break;
31731                 default:
31732                 this.setSize(sz.width,  sz.height);
31733             }
31734         }
31735         this.el.alignTo(this.boundEl, this.alignment);
31736         this.editing = true;
31737         if(Roo.QuickTips){
31738             Roo.QuickTips.disable();
31739         }
31740         this.show();
31741     },
31742
31743     /**
31744      * Sets the height and width of this editor.
31745      * @param {Number} width The new width
31746      * @param {Number} height The new height
31747      */
31748     setSize : function(w, h){
31749         this.field.setSize(w, h);
31750         if(this.el){
31751             this.el.sync();
31752         }
31753     },
31754
31755     /**
31756      * Realigns the editor to the bound field based on the current alignment config value.
31757      */
31758     realign : function(){
31759         this.el.alignTo(this.boundEl, this.alignment);
31760     },
31761
31762     /**
31763      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31764      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31765      */
31766     completeEdit : function(remainVisible){
31767         if(!this.editing){
31768             return;
31769         }
31770         var v = this.getValue();
31771         if(this.revertInvalid !== false && !this.field.isValid()){
31772             v = this.startValue;
31773             this.cancelEdit(true);
31774         }
31775         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31776             this.editing = false;
31777             this.hide();
31778             return;
31779         }
31780         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31781             this.editing = false;
31782             if(this.updateEl && this.boundEl){
31783                 this.boundEl.update(v);
31784             }
31785             if(remainVisible !== true){
31786                 this.hide();
31787             }
31788             this.fireEvent("complete", this, v, this.startValue);
31789         }
31790     },
31791
31792     // private
31793     onShow : function(){
31794         this.el.show();
31795         if(this.hideEl !== false){
31796             this.boundEl.hide();
31797         }
31798         this.field.show();
31799         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31800             this.fixIEFocus = true;
31801             this.deferredFocus.defer(50, this);
31802         }else{
31803             this.field.focus();
31804         }
31805         this.fireEvent("startedit", this.boundEl, this.startValue);
31806     },
31807
31808     deferredFocus : function(){
31809         if(this.editing){
31810             this.field.focus();
31811         }
31812     },
31813
31814     /**
31815      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31816      * reverted to the original starting value.
31817      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31818      * cancel (defaults to false)
31819      */
31820     cancelEdit : function(remainVisible){
31821         if(this.editing){
31822             this.setValue(this.startValue);
31823             if(remainVisible !== true){
31824                 this.hide();
31825             }
31826         }
31827     },
31828
31829     // private
31830     onBlur : function(){
31831         if(this.allowBlur !== true && this.editing){
31832             this.completeEdit();
31833         }
31834     },
31835
31836     // private
31837     onHide : function(){
31838         if(this.editing){
31839             this.completeEdit();
31840             return;
31841         }
31842         this.field.blur();
31843         if(this.field.collapse){
31844             this.field.collapse();
31845         }
31846         this.el.hide();
31847         if(this.hideEl !== false){
31848             this.boundEl.show();
31849         }
31850         if(Roo.QuickTips){
31851             Roo.QuickTips.enable();
31852         }
31853     },
31854
31855     /**
31856      * Sets the data value of the editor
31857      * @param {Mixed} value Any valid value supported by the underlying field
31858      */
31859     setValue : function(v){
31860         this.field.setValue(v);
31861     },
31862
31863     /**
31864      * Gets the data value of the editor
31865      * @return {Mixed} The data value
31866      */
31867     getValue : function(){
31868         return this.field.getValue();
31869     }
31870 });/*
31871  * Based on:
31872  * Ext JS Library 1.1.1
31873  * Copyright(c) 2006-2007, Ext JS, LLC.
31874  *
31875  * Originally Released Under LGPL - original licence link has changed is not relivant.
31876  *
31877  * Fork - LGPL
31878  * <script type="text/javascript">
31879  */
31880  
31881 /**
31882  * @class Roo.BasicDialog
31883  * @extends Roo.util.Observable
31884  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31885  * <pre><code>
31886 var dlg = new Roo.BasicDialog("my-dlg", {
31887     height: 200,
31888     width: 300,
31889     minHeight: 100,
31890     minWidth: 150,
31891     modal: true,
31892     proxyDrag: true,
31893     shadow: true
31894 });
31895 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31896 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31897 dlg.addButton('Cancel', dlg.hide, dlg);
31898 dlg.show();
31899 </code></pre>
31900   <b>A Dialog should always be a direct child of the body element.</b>
31901  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31902  * @cfg {String} title Default text to display in the title bar (defaults to null)
31903  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31904  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31905  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31906  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31907  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31908  * (defaults to null with no animation)
31909  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31910  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31911  * property for valid values (defaults to 'all')
31912  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31913  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31914  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31915  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31916  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31917  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31918  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31919  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31920  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31921  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31922  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31923  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31924  * draggable = true (defaults to false)
31925  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31926  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31927  * shadow (defaults to false)
31928  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31929  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31930  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31931  * @cfg {Array} buttons Array of buttons
31932  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31933  * @constructor
31934  * Create a new BasicDialog.
31935  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31936  * @param {Object} config Configuration options
31937  */
31938 Roo.BasicDialog = function(el, config){
31939     this.el = Roo.get(el);
31940     var dh = Roo.DomHelper;
31941     if(!this.el && config && config.autoCreate){
31942         if(typeof config.autoCreate == "object"){
31943             if(!config.autoCreate.id){
31944                 config.autoCreate.id = el;
31945             }
31946             this.el = dh.append(document.body,
31947                         config.autoCreate, true);
31948         }else{
31949             this.el = dh.append(document.body,
31950                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31951         }
31952     }
31953     el = this.el;
31954     el.setDisplayed(true);
31955     el.hide = this.hideAction;
31956     this.id = el.id;
31957     el.addClass("x-dlg");
31958
31959     Roo.apply(this, config);
31960
31961     this.proxy = el.createProxy("x-dlg-proxy");
31962     this.proxy.hide = this.hideAction;
31963     this.proxy.setOpacity(.5);
31964     this.proxy.hide();
31965
31966     if(config.width){
31967         el.setWidth(config.width);
31968     }
31969     if(config.height){
31970         el.setHeight(config.height);
31971     }
31972     this.size = el.getSize();
31973     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31974         this.xy = [config.x,config.y];
31975     }else{
31976         this.xy = el.getCenterXY(true);
31977     }
31978     /** The header element @type Roo.Element */
31979     this.header = el.child("> .x-dlg-hd");
31980     /** The body element @type Roo.Element */
31981     this.body = el.child("> .x-dlg-bd");
31982     /** The footer element @type Roo.Element */
31983     this.footer = el.child("> .x-dlg-ft");
31984
31985     if(!this.header){
31986         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31987     }
31988     if(!this.body){
31989         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31990     }
31991
31992     this.header.unselectable();
31993     if(this.title){
31994         this.header.update(this.title);
31995     }
31996     // this element allows the dialog to be focused for keyboard event
31997     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31998     this.focusEl.swallowEvent("click", true);
31999
32000     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32001
32002     // wrap the body and footer for special rendering
32003     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32004     if(this.footer){
32005         this.bwrap.dom.appendChild(this.footer.dom);
32006     }
32007
32008     this.bg = this.el.createChild({
32009         tag: "div", cls:"x-dlg-bg",
32010         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32011     });
32012     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32013
32014
32015     if(this.autoScroll !== false && !this.autoTabs){
32016         this.body.setStyle("overflow", "auto");
32017     }
32018
32019     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32020
32021     if(this.closable !== false){
32022         this.el.addClass("x-dlg-closable");
32023         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32024         this.close.on("click", this.closeClick, this);
32025         this.close.addClassOnOver("x-dlg-close-over");
32026     }
32027     if(this.collapsible !== false){
32028         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32029         this.collapseBtn.on("click", this.collapseClick, this);
32030         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32031         this.header.on("dblclick", this.collapseClick, this);
32032     }
32033     if(this.resizable !== false){
32034         this.el.addClass("x-dlg-resizable");
32035         this.resizer = new Roo.Resizable(el, {
32036             minWidth: this.minWidth || 80,
32037             minHeight:this.minHeight || 80,
32038             handles: this.resizeHandles || "all",
32039             pinned: true
32040         });
32041         this.resizer.on("beforeresize", this.beforeResize, this);
32042         this.resizer.on("resize", this.onResize, this);
32043     }
32044     if(this.draggable !== false){
32045         el.addClass("x-dlg-draggable");
32046         if (!this.proxyDrag) {
32047             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32048         }
32049         else {
32050             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32051         }
32052         dd.setHandleElId(this.header.id);
32053         dd.endDrag = this.endMove.createDelegate(this);
32054         dd.startDrag = this.startMove.createDelegate(this);
32055         dd.onDrag = this.onDrag.createDelegate(this);
32056         dd.scroll = false;
32057         this.dd = dd;
32058     }
32059     if(this.modal){
32060         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32061         this.mask.enableDisplayMode("block");
32062         this.mask.hide();
32063         this.el.addClass("x-dlg-modal");
32064     }
32065     if(this.shadow){
32066         this.shadow = new Roo.Shadow({
32067             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32068             offset : this.shadowOffset
32069         });
32070     }else{
32071         this.shadowOffset = 0;
32072     }
32073     if(Roo.useShims && this.shim !== false){
32074         this.shim = this.el.createShim();
32075         this.shim.hide = this.hideAction;
32076         this.shim.hide();
32077     }else{
32078         this.shim = false;
32079     }
32080     if(this.autoTabs){
32081         this.initTabs();
32082     }
32083     if (this.buttons) { 
32084         var bts= this.buttons;
32085         this.buttons = [];
32086         Roo.each(bts, function(b) {
32087             this.addButton(b);
32088         }, this);
32089     }
32090     
32091     
32092     this.addEvents({
32093         /**
32094          * @event keydown
32095          * Fires when a key is pressed
32096          * @param {Roo.BasicDialog} this
32097          * @param {Roo.EventObject} e
32098          */
32099         "keydown" : true,
32100         /**
32101          * @event move
32102          * Fires when this dialog is moved by the user.
32103          * @param {Roo.BasicDialog} this
32104          * @param {Number} x The new page X
32105          * @param {Number} y The new page Y
32106          */
32107         "move" : true,
32108         /**
32109          * @event resize
32110          * Fires when this dialog is resized by the user.
32111          * @param {Roo.BasicDialog} this
32112          * @param {Number} width The new width
32113          * @param {Number} height The new height
32114          */
32115         "resize" : true,
32116         /**
32117          * @event beforehide
32118          * Fires before this dialog is hidden.
32119          * @param {Roo.BasicDialog} this
32120          */
32121         "beforehide" : true,
32122         /**
32123          * @event hide
32124          * Fires when this dialog is hidden.
32125          * @param {Roo.BasicDialog} this
32126          */
32127         "hide" : true,
32128         /**
32129          * @event beforeshow
32130          * Fires before this dialog is shown.
32131          * @param {Roo.BasicDialog} this
32132          */
32133         "beforeshow" : true,
32134         /**
32135          * @event show
32136          * Fires when this dialog is shown.
32137          * @param {Roo.BasicDialog} this
32138          */
32139         "show" : true
32140     });
32141     el.on("keydown", this.onKeyDown, this);
32142     el.on("mousedown", this.toFront, this);
32143     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32144     this.el.hide();
32145     Roo.DialogManager.register(this);
32146     Roo.BasicDialog.superclass.constructor.call(this);
32147 };
32148
32149 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32150     shadowOffset: Roo.isIE ? 6 : 5,
32151     minHeight: 80,
32152     minWidth: 200,
32153     minButtonWidth: 75,
32154     defaultButton: null,
32155     buttonAlign: "right",
32156     tabTag: 'div',
32157     firstShow: true,
32158
32159     /**
32160      * Sets the dialog title text
32161      * @param {String} text The title text to display
32162      * @return {Roo.BasicDialog} this
32163      */
32164     setTitle : function(text){
32165         this.header.update(text);
32166         return this;
32167     },
32168
32169     // private
32170     closeClick : function(){
32171         this.hide();
32172     },
32173
32174     // private
32175     collapseClick : function(){
32176         this[this.collapsed ? "expand" : "collapse"]();
32177     },
32178
32179     /**
32180      * Collapses the dialog to its minimized state (only the title bar is visible).
32181      * Equivalent to the user clicking the collapse dialog button.
32182      */
32183     collapse : function(){
32184         if(!this.collapsed){
32185             this.collapsed = true;
32186             this.el.addClass("x-dlg-collapsed");
32187             this.restoreHeight = this.el.getHeight();
32188             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32189         }
32190     },
32191
32192     /**
32193      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32194      * clicking the expand dialog button.
32195      */
32196     expand : function(){
32197         if(this.collapsed){
32198             this.collapsed = false;
32199             this.el.removeClass("x-dlg-collapsed");
32200             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32201         }
32202     },
32203
32204     /**
32205      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32206      * @return {Roo.TabPanel} The tabs component
32207      */
32208     initTabs : function(){
32209         var tabs = this.getTabs();
32210         while(tabs.getTab(0)){
32211             tabs.removeTab(0);
32212         }
32213         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32214             var dom = el.dom;
32215             tabs.addTab(Roo.id(dom), dom.title);
32216             dom.title = "";
32217         });
32218         tabs.activate(0);
32219         return tabs;
32220     },
32221
32222     // private
32223     beforeResize : function(){
32224         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32225     },
32226
32227     // private
32228     onResize : function(){
32229         this.refreshSize();
32230         this.syncBodyHeight();
32231         this.adjustAssets();
32232         this.focus();
32233         this.fireEvent("resize", this, this.size.width, this.size.height);
32234     },
32235
32236     // private
32237     onKeyDown : function(e){
32238         if(this.isVisible()){
32239             this.fireEvent("keydown", this, e);
32240         }
32241     },
32242
32243     /**
32244      * Resizes the dialog.
32245      * @param {Number} width
32246      * @param {Number} height
32247      * @return {Roo.BasicDialog} this
32248      */
32249     resizeTo : function(width, height){
32250         this.el.setSize(width, height);
32251         this.size = {width: width, height: height};
32252         this.syncBodyHeight();
32253         if(this.fixedcenter){
32254             this.center();
32255         }
32256         if(this.isVisible()){
32257             this.constrainXY();
32258             this.adjustAssets();
32259         }
32260         this.fireEvent("resize", this, width, height);
32261         return this;
32262     },
32263
32264
32265     /**
32266      * Resizes the dialog to fit the specified content size.
32267      * @param {Number} width
32268      * @param {Number} height
32269      * @return {Roo.BasicDialog} this
32270      */
32271     setContentSize : function(w, h){
32272         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32273         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32274         //if(!this.el.isBorderBox()){
32275             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32276             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32277         //}
32278         if(this.tabs){
32279             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32280             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32281         }
32282         this.resizeTo(w, h);
32283         return this;
32284     },
32285
32286     /**
32287      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32288      * executed in response to a particular key being pressed while the dialog is active.
32289      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32290      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32291      * @param {Function} fn The function to call
32292      * @param {Object} scope (optional) The scope of the function
32293      * @return {Roo.BasicDialog} this
32294      */
32295     addKeyListener : function(key, fn, scope){
32296         var keyCode, shift, ctrl, alt;
32297         if(typeof key == "object" && !(key instanceof Array)){
32298             keyCode = key["key"];
32299             shift = key["shift"];
32300             ctrl = key["ctrl"];
32301             alt = key["alt"];
32302         }else{
32303             keyCode = key;
32304         }
32305         var handler = function(dlg, e){
32306             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32307                 var k = e.getKey();
32308                 if(keyCode instanceof Array){
32309                     for(var i = 0, len = keyCode.length; i < len; i++){
32310                         if(keyCode[i] == k){
32311                           fn.call(scope || window, dlg, k, e);
32312                           return;
32313                         }
32314                     }
32315                 }else{
32316                     if(k == keyCode){
32317                         fn.call(scope || window, dlg, k, e);
32318                     }
32319                 }
32320             }
32321         };
32322         this.on("keydown", handler);
32323         return this;
32324     },
32325
32326     /**
32327      * Returns the TabPanel component (creates it if it doesn't exist).
32328      * Note: If you wish to simply check for the existence of tabs without creating them,
32329      * check for a null 'tabs' property.
32330      * @return {Roo.TabPanel} The tabs component
32331      */
32332     getTabs : function(){
32333         if(!this.tabs){
32334             this.el.addClass("x-dlg-auto-tabs");
32335             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32336             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32337         }
32338         return this.tabs;
32339     },
32340
32341     /**
32342      * Adds a button to the footer section of the dialog.
32343      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32344      * object or a valid Roo.DomHelper element config
32345      * @param {Function} handler The function called when the button is clicked
32346      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32347      * @return {Roo.Button} The new button
32348      */
32349     addButton : function(config, handler, scope){
32350         var dh = Roo.DomHelper;
32351         if(!this.footer){
32352             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32353         }
32354         if(!this.btnContainer){
32355             var tb = this.footer.createChild({
32356
32357                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32358                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32359             }, null, true);
32360             this.btnContainer = tb.firstChild.firstChild.firstChild;
32361         }
32362         var bconfig = {
32363             handler: handler,
32364             scope: scope,
32365             minWidth: this.minButtonWidth,
32366             hideParent:true
32367         };
32368         if(typeof config == "string"){
32369             bconfig.text = config;
32370         }else{
32371             if(config.tag){
32372                 bconfig.dhconfig = config;
32373             }else{
32374                 Roo.apply(bconfig, config);
32375             }
32376         }
32377         var fc = false;
32378         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32379             bconfig.position = Math.max(0, bconfig.position);
32380             fc = this.btnContainer.childNodes[bconfig.position];
32381         }
32382          
32383         var btn = new Roo.Button(
32384             fc ? 
32385                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32386                 : this.btnContainer.appendChild(document.createElement("td")),
32387             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32388             bconfig
32389         );
32390         this.syncBodyHeight();
32391         if(!this.buttons){
32392             /**
32393              * Array of all the buttons that have been added to this dialog via addButton
32394              * @type Array
32395              */
32396             this.buttons = [];
32397         }
32398         this.buttons.push(btn);
32399         return btn;
32400     },
32401
32402     /**
32403      * Sets the default button to be focused when the dialog is displayed.
32404      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32405      * @return {Roo.BasicDialog} this
32406      */
32407     setDefaultButton : function(btn){
32408         this.defaultButton = btn;
32409         return this;
32410     },
32411
32412     // private
32413     getHeaderFooterHeight : function(safe){
32414         var height = 0;
32415         if(this.header){
32416            height += this.header.getHeight();
32417         }
32418         if(this.footer){
32419            var fm = this.footer.getMargins();
32420             height += (this.footer.getHeight()+fm.top+fm.bottom);
32421         }
32422         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32423         height += this.centerBg.getPadding("tb");
32424         return height;
32425     },
32426
32427     // private
32428     syncBodyHeight : function()
32429     {
32430         var bd = this.body, // the text
32431             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32432             bw = this.bwrap;
32433         var height = this.size.height - this.getHeaderFooterHeight(false);
32434         bd.setHeight(height-bd.getMargins("tb"));
32435         var hh = this.header.getHeight();
32436         var h = this.size.height-hh;
32437         cb.setHeight(h);
32438         
32439         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32440         bw.setHeight(h-cb.getPadding("tb"));
32441         
32442         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32443         bd.setWidth(bw.getWidth(true));
32444         if(this.tabs){
32445             this.tabs.syncHeight();
32446             if(Roo.isIE){
32447                 this.tabs.el.repaint();
32448             }
32449         }
32450     },
32451
32452     /**
32453      * Restores the previous state of the dialog if Roo.state is configured.
32454      * @return {Roo.BasicDialog} this
32455      */
32456     restoreState : function(){
32457         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32458         if(box && box.width){
32459             this.xy = [box.x, box.y];
32460             this.resizeTo(box.width, box.height);
32461         }
32462         return this;
32463     },
32464
32465     // private
32466     beforeShow : function(){
32467         this.expand();
32468         if(this.fixedcenter){
32469             this.xy = this.el.getCenterXY(true);
32470         }
32471         if(this.modal){
32472             Roo.get(document.body).addClass("x-body-masked");
32473             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32474             this.mask.show();
32475         }
32476         this.constrainXY();
32477     },
32478
32479     // private
32480     animShow : function(){
32481         var b = Roo.get(this.animateTarget).getBox();
32482         this.proxy.setSize(b.width, b.height);
32483         this.proxy.setLocation(b.x, b.y);
32484         this.proxy.show();
32485         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32486                     true, .35, this.showEl.createDelegate(this));
32487     },
32488
32489     /**
32490      * Shows the dialog.
32491      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32492      * @return {Roo.BasicDialog} this
32493      */
32494     show : function(animateTarget){
32495         if (this.fireEvent("beforeshow", this) === false){
32496             return;
32497         }
32498         if(this.syncHeightBeforeShow){
32499             this.syncBodyHeight();
32500         }else if(this.firstShow){
32501             this.firstShow = false;
32502             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32503         }
32504         this.animateTarget = animateTarget || this.animateTarget;
32505         if(!this.el.isVisible()){
32506             this.beforeShow();
32507             if(this.animateTarget && Roo.get(this.animateTarget)){
32508                 this.animShow();
32509             }else{
32510                 this.showEl();
32511             }
32512         }
32513         return this;
32514     },
32515
32516     // private
32517     showEl : function(){
32518         this.proxy.hide();
32519         this.el.setXY(this.xy);
32520         this.el.show();
32521         this.adjustAssets(true);
32522         this.toFront();
32523         this.focus();
32524         // IE peekaboo bug - fix found by Dave Fenwick
32525         if(Roo.isIE){
32526             this.el.repaint();
32527         }
32528         this.fireEvent("show", this);
32529     },
32530
32531     /**
32532      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32533      * dialog itself will receive focus.
32534      */
32535     focus : function(){
32536         if(this.defaultButton){
32537             this.defaultButton.focus();
32538         }else{
32539             this.focusEl.focus();
32540         }
32541     },
32542
32543     // private
32544     constrainXY : function(){
32545         if(this.constraintoviewport !== false){
32546             if(!this.viewSize){
32547                 if(this.container){
32548                     var s = this.container.getSize();
32549                     this.viewSize = [s.width, s.height];
32550                 }else{
32551                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32552                 }
32553             }
32554             var s = Roo.get(this.container||document).getScroll();
32555
32556             var x = this.xy[0], y = this.xy[1];
32557             var w = this.size.width, h = this.size.height;
32558             var vw = this.viewSize[0], vh = this.viewSize[1];
32559             // only move it if it needs it
32560             var moved = false;
32561             // first validate right/bottom
32562             if(x + w > vw+s.left){
32563                 x = vw - w;
32564                 moved = true;
32565             }
32566             if(y + h > vh+s.top){
32567                 y = vh - h;
32568                 moved = true;
32569             }
32570             // then make sure top/left isn't negative
32571             if(x < s.left){
32572                 x = s.left;
32573                 moved = true;
32574             }
32575             if(y < s.top){
32576                 y = s.top;
32577                 moved = true;
32578             }
32579             if(moved){
32580                 // cache xy
32581                 this.xy = [x, y];
32582                 if(this.isVisible()){
32583                     this.el.setLocation(x, y);
32584                     this.adjustAssets();
32585                 }
32586             }
32587         }
32588     },
32589
32590     // private
32591     onDrag : function(){
32592         if(!this.proxyDrag){
32593             this.xy = this.el.getXY();
32594             this.adjustAssets();
32595         }
32596     },
32597
32598     // private
32599     adjustAssets : function(doShow){
32600         var x = this.xy[0], y = this.xy[1];
32601         var w = this.size.width, h = this.size.height;
32602         if(doShow === true){
32603             if(this.shadow){
32604                 this.shadow.show(this.el);
32605             }
32606             if(this.shim){
32607                 this.shim.show();
32608             }
32609         }
32610         if(this.shadow && this.shadow.isVisible()){
32611             this.shadow.show(this.el);
32612         }
32613         if(this.shim && this.shim.isVisible()){
32614             this.shim.setBounds(x, y, w, h);
32615         }
32616     },
32617
32618     // private
32619     adjustViewport : function(w, h){
32620         if(!w || !h){
32621             w = Roo.lib.Dom.getViewWidth();
32622             h = Roo.lib.Dom.getViewHeight();
32623         }
32624         // cache the size
32625         this.viewSize = [w, h];
32626         if(this.modal && this.mask.isVisible()){
32627             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32628             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32629         }
32630         if(this.isVisible()){
32631             this.constrainXY();
32632         }
32633     },
32634
32635     /**
32636      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32637      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32638      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32639      */
32640     destroy : function(removeEl){
32641         if(this.isVisible()){
32642             this.animateTarget = null;
32643             this.hide();
32644         }
32645         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32646         if(this.tabs){
32647             this.tabs.destroy(removeEl);
32648         }
32649         Roo.destroy(
32650              this.shim,
32651              this.proxy,
32652              this.resizer,
32653              this.close,
32654              this.mask
32655         );
32656         if(this.dd){
32657             this.dd.unreg();
32658         }
32659         if(this.buttons){
32660            for(var i = 0, len = this.buttons.length; i < len; i++){
32661                this.buttons[i].destroy();
32662            }
32663         }
32664         this.el.removeAllListeners();
32665         if(removeEl === true){
32666             this.el.update("");
32667             this.el.remove();
32668         }
32669         Roo.DialogManager.unregister(this);
32670     },
32671
32672     // private
32673     startMove : function(){
32674         if(this.proxyDrag){
32675             this.proxy.show();
32676         }
32677         if(this.constraintoviewport !== false){
32678             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32679         }
32680     },
32681
32682     // private
32683     endMove : function(){
32684         if(!this.proxyDrag){
32685             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32686         }else{
32687             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32688             this.proxy.hide();
32689         }
32690         this.refreshSize();
32691         this.adjustAssets();
32692         this.focus();
32693         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32694     },
32695
32696     /**
32697      * Brings this dialog to the front of any other visible dialogs
32698      * @return {Roo.BasicDialog} this
32699      */
32700     toFront : function(){
32701         Roo.DialogManager.bringToFront(this);
32702         return this;
32703     },
32704
32705     /**
32706      * Sends this dialog to the back (under) of any other visible dialogs
32707      * @return {Roo.BasicDialog} this
32708      */
32709     toBack : function(){
32710         Roo.DialogManager.sendToBack(this);
32711         return this;
32712     },
32713
32714     /**
32715      * Centers this dialog in the viewport
32716      * @return {Roo.BasicDialog} this
32717      */
32718     center : function(){
32719         var xy = this.el.getCenterXY(true);
32720         this.moveTo(xy[0], xy[1]);
32721         return this;
32722     },
32723
32724     /**
32725      * Moves the dialog's top-left corner to the specified point
32726      * @param {Number} x
32727      * @param {Number} y
32728      * @return {Roo.BasicDialog} this
32729      */
32730     moveTo : function(x, y){
32731         this.xy = [x,y];
32732         if(this.isVisible()){
32733             this.el.setXY(this.xy);
32734             this.adjustAssets();
32735         }
32736         return this;
32737     },
32738
32739     /**
32740      * Aligns the dialog to the specified element
32741      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32742      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32743      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32744      * @return {Roo.BasicDialog} this
32745      */
32746     alignTo : function(element, position, offsets){
32747         this.xy = this.el.getAlignToXY(element, position, offsets);
32748         if(this.isVisible()){
32749             this.el.setXY(this.xy);
32750             this.adjustAssets();
32751         }
32752         return this;
32753     },
32754
32755     /**
32756      * Anchors an element to another element and realigns it when the window is resized.
32757      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32758      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32759      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32760      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32761      * is a number, it is used as the buffer delay (defaults to 50ms).
32762      * @return {Roo.BasicDialog} this
32763      */
32764     anchorTo : function(el, alignment, offsets, monitorScroll){
32765         var action = function(){
32766             this.alignTo(el, alignment, offsets);
32767         };
32768         Roo.EventManager.onWindowResize(action, this);
32769         var tm = typeof monitorScroll;
32770         if(tm != 'undefined'){
32771             Roo.EventManager.on(window, 'scroll', action, this,
32772                 {buffer: tm == 'number' ? monitorScroll : 50});
32773         }
32774         action.call(this);
32775         return this;
32776     },
32777
32778     /**
32779      * Returns true if the dialog is visible
32780      * @return {Boolean}
32781      */
32782     isVisible : function(){
32783         return this.el.isVisible();
32784     },
32785
32786     // private
32787     animHide : function(callback){
32788         var b = Roo.get(this.animateTarget).getBox();
32789         this.proxy.show();
32790         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32791         this.el.hide();
32792         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32793                     this.hideEl.createDelegate(this, [callback]));
32794     },
32795
32796     /**
32797      * Hides the dialog.
32798      * @param {Function} callback (optional) Function to call when the dialog is hidden
32799      * @return {Roo.BasicDialog} this
32800      */
32801     hide : function(callback){
32802         if (this.fireEvent("beforehide", this) === false){
32803             return;
32804         }
32805         if(this.shadow){
32806             this.shadow.hide();
32807         }
32808         if(this.shim) {
32809           this.shim.hide();
32810         }
32811         // sometimes animateTarget seems to get set.. causing problems...
32812         // this just double checks..
32813         if(this.animateTarget && Roo.get(this.animateTarget)) {
32814            this.animHide(callback);
32815         }else{
32816             this.el.hide();
32817             this.hideEl(callback);
32818         }
32819         return this;
32820     },
32821
32822     // private
32823     hideEl : function(callback){
32824         this.proxy.hide();
32825         if(this.modal){
32826             this.mask.hide();
32827             Roo.get(document.body).removeClass("x-body-masked");
32828         }
32829         this.fireEvent("hide", this);
32830         if(typeof callback == "function"){
32831             callback();
32832         }
32833     },
32834
32835     // private
32836     hideAction : function(){
32837         this.setLeft("-10000px");
32838         this.setTop("-10000px");
32839         this.setStyle("visibility", "hidden");
32840     },
32841
32842     // private
32843     refreshSize : function(){
32844         this.size = this.el.getSize();
32845         this.xy = this.el.getXY();
32846         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32847     },
32848
32849     // private
32850     // z-index is managed by the DialogManager and may be overwritten at any time
32851     setZIndex : function(index){
32852         if(this.modal){
32853             this.mask.setStyle("z-index", index);
32854         }
32855         if(this.shim){
32856             this.shim.setStyle("z-index", ++index);
32857         }
32858         if(this.shadow){
32859             this.shadow.setZIndex(++index);
32860         }
32861         this.el.setStyle("z-index", ++index);
32862         if(this.proxy){
32863             this.proxy.setStyle("z-index", ++index);
32864         }
32865         if(this.resizer){
32866             this.resizer.proxy.setStyle("z-index", ++index);
32867         }
32868
32869         this.lastZIndex = index;
32870     },
32871
32872     /**
32873      * Returns the element for this dialog
32874      * @return {Roo.Element} The underlying dialog Element
32875      */
32876     getEl : function(){
32877         return this.el;
32878     }
32879 });
32880
32881 /**
32882  * @class Roo.DialogManager
32883  * Provides global access to BasicDialogs that have been created and
32884  * support for z-indexing (layering) multiple open dialogs.
32885  */
32886 Roo.DialogManager = function(){
32887     var list = {};
32888     var accessList = [];
32889     var front = null;
32890
32891     // private
32892     var sortDialogs = function(d1, d2){
32893         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32894     };
32895
32896     // private
32897     var orderDialogs = function(){
32898         accessList.sort(sortDialogs);
32899         var seed = Roo.DialogManager.zseed;
32900         for(var i = 0, len = accessList.length; i < len; i++){
32901             var dlg = accessList[i];
32902             if(dlg){
32903                 dlg.setZIndex(seed + (i*10));
32904             }
32905         }
32906     };
32907
32908     return {
32909         /**
32910          * The starting z-index for BasicDialogs (defaults to 9000)
32911          * @type Number The z-index value
32912          */
32913         zseed : 9000,
32914
32915         // private
32916         register : function(dlg){
32917             list[dlg.id] = dlg;
32918             accessList.push(dlg);
32919         },
32920
32921         // private
32922         unregister : function(dlg){
32923             delete list[dlg.id];
32924             var i=0;
32925             var len=0;
32926             if(!accessList.indexOf){
32927                 for(  i = 0, len = accessList.length; i < len; i++){
32928                     if(accessList[i] == dlg){
32929                         accessList.splice(i, 1);
32930                         return;
32931                     }
32932                 }
32933             }else{
32934                  i = accessList.indexOf(dlg);
32935                 if(i != -1){
32936                     accessList.splice(i, 1);
32937                 }
32938             }
32939         },
32940
32941         /**
32942          * Gets a registered dialog by id
32943          * @param {String/Object} id The id of the dialog or a dialog
32944          * @return {Roo.BasicDialog} this
32945          */
32946         get : function(id){
32947             return typeof id == "object" ? id : list[id];
32948         },
32949
32950         /**
32951          * Brings the specified dialog to the front
32952          * @param {String/Object} dlg The id of the dialog or a dialog
32953          * @return {Roo.BasicDialog} this
32954          */
32955         bringToFront : function(dlg){
32956             dlg = this.get(dlg);
32957             if(dlg != front){
32958                 front = dlg;
32959                 dlg._lastAccess = new Date().getTime();
32960                 orderDialogs();
32961             }
32962             return dlg;
32963         },
32964
32965         /**
32966          * Sends the specified dialog to the back
32967          * @param {String/Object} dlg The id of the dialog or a dialog
32968          * @return {Roo.BasicDialog} this
32969          */
32970         sendToBack : function(dlg){
32971             dlg = this.get(dlg);
32972             dlg._lastAccess = -(new Date().getTime());
32973             orderDialogs();
32974             return dlg;
32975         },
32976
32977         /**
32978          * Hides all dialogs
32979          */
32980         hideAll : function(){
32981             for(var id in list){
32982                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32983                     list[id].hide();
32984                 }
32985             }
32986         }
32987     };
32988 }();
32989
32990 /**
32991  * @class Roo.LayoutDialog
32992  * @extends Roo.BasicDialog
32993  * Dialog which provides adjustments for working with a layout in a Dialog.
32994  * Add your necessary layout config options to the dialog's config.<br>
32995  * Example usage (including a nested layout):
32996  * <pre><code>
32997 if(!dialog){
32998     dialog = new Roo.LayoutDialog("download-dlg", {
32999         modal: true,
33000         width:600,
33001         height:450,
33002         shadow:true,
33003         minWidth:500,
33004         minHeight:350,
33005         autoTabs:true,
33006         proxyDrag:true,
33007         // layout config merges with the dialog config
33008         center:{
33009             tabPosition: "top",
33010             alwaysShowTabs: true
33011         }
33012     });
33013     dialog.addKeyListener(27, dialog.hide, dialog);
33014     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33015     dialog.addButton("Build It!", this.getDownload, this);
33016
33017     // we can even add nested layouts
33018     var innerLayout = new Roo.BorderLayout("dl-inner", {
33019         east: {
33020             initialSize: 200,
33021             autoScroll:true,
33022             split:true
33023         },
33024         center: {
33025             autoScroll:true
33026         }
33027     });
33028     innerLayout.beginUpdate();
33029     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33030     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33031     innerLayout.endUpdate(true);
33032
33033     var layout = dialog.getLayout();
33034     layout.beginUpdate();
33035     layout.add("center", new Roo.ContentPanel("standard-panel",
33036                         {title: "Download the Source", fitToFrame:true}));
33037     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33038                {title: "Build your own roo.js"}));
33039     layout.getRegion("center").showPanel(sp);
33040     layout.endUpdate();
33041 }
33042 </code></pre>
33043     * @constructor
33044     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33045     * @param {Object} config configuration options
33046   */
33047 Roo.LayoutDialog = function(el, cfg){
33048     
33049     var config=  cfg;
33050     if (typeof(cfg) == 'undefined') {
33051         config = Roo.apply({}, el);
33052         // not sure why we use documentElement here.. - it should always be body.
33053         // IE7 borks horribly if we use documentElement.
33054         // webkit also does not like documentElement - it creates a body element...
33055         el = Roo.get( document.body || document.documentElement ).createChild();
33056         //config.autoCreate = true;
33057     }
33058     
33059     
33060     config.autoTabs = false;
33061     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33062     this.body.setStyle({overflow:"hidden", position:"relative"});
33063     this.layout = new Roo.BorderLayout(this.body.dom, config);
33064     this.layout.monitorWindowResize = false;
33065     this.el.addClass("x-dlg-auto-layout");
33066     // fix case when center region overwrites center function
33067     this.center = Roo.BasicDialog.prototype.center;
33068     this.on("show", this.layout.layout, this.layout, true);
33069     if (config.items) {
33070         var xitems = config.items;
33071         delete config.items;
33072         Roo.each(xitems, this.addxtype, this);
33073     }
33074     
33075     
33076 };
33077 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33078     /**
33079      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33080      * @deprecated
33081      */
33082     endUpdate : function(){
33083         this.layout.endUpdate();
33084     },
33085
33086     /**
33087      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33088      *  @deprecated
33089      */
33090     beginUpdate : function(){
33091         this.layout.beginUpdate();
33092     },
33093
33094     /**
33095      * Get the BorderLayout for this dialog
33096      * @return {Roo.BorderLayout}
33097      */
33098     getLayout : function(){
33099         return this.layout;
33100     },
33101
33102     showEl : function(){
33103         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33104         if(Roo.isIE7){
33105             this.layout.layout();
33106         }
33107     },
33108
33109     // private
33110     // Use the syncHeightBeforeShow config option to control this automatically
33111     syncBodyHeight : function(){
33112         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33113         if(this.layout){this.layout.layout();}
33114     },
33115     
33116       /**
33117      * Add an xtype element (actually adds to the layout.)
33118      * @return {Object} xdata xtype object data.
33119      */
33120     
33121     addxtype : function(c) {
33122         return this.layout.addxtype(c);
33123     }
33124 });/*
33125  * Based on:
33126  * Ext JS Library 1.1.1
33127  * Copyright(c) 2006-2007, Ext JS, LLC.
33128  *
33129  * Originally Released Under LGPL - original licence link has changed is not relivant.
33130  *
33131  * Fork - LGPL
33132  * <script type="text/javascript">
33133  */
33134  
33135 /**
33136  * @class Roo.MessageBox
33137  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33138  * Example usage:
33139  *<pre><code>
33140 // Basic alert:
33141 Roo.Msg.alert('Status', 'Changes saved successfully.');
33142
33143 // Prompt for user data:
33144 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33145     if (btn == 'ok'){
33146         // process text value...
33147     }
33148 });
33149
33150 // Show a dialog using config options:
33151 Roo.Msg.show({
33152    title:'Save Changes?',
33153    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33154    buttons: Roo.Msg.YESNOCANCEL,
33155    fn: processResult,
33156    animEl: 'elId'
33157 });
33158 </code></pre>
33159  * @singleton
33160  */
33161 Roo.MessageBox = function(){
33162     var dlg, opt, mask, waitTimer;
33163     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33164     var buttons, activeTextEl, bwidth;
33165
33166     // private
33167     var handleButton = function(button){
33168         dlg.hide();
33169         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33170     };
33171
33172     // private
33173     var handleHide = function(){
33174         if(opt && opt.cls){
33175             dlg.el.removeClass(opt.cls);
33176         }
33177         if(waitTimer){
33178             Roo.TaskMgr.stop(waitTimer);
33179             waitTimer = null;
33180         }
33181     };
33182
33183     // private
33184     var updateButtons = function(b){
33185         var width = 0;
33186         if(!b){
33187             buttons["ok"].hide();
33188             buttons["cancel"].hide();
33189             buttons["yes"].hide();
33190             buttons["no"].hide();
33191             dlg.footer.dom.style.display = 'none';
33192             return width;
33193         }
33194         dlg.footer.dom.style.display = '';
33195         for(var k in buttons){
33196             if(typeof buttons[k] != "function"){
33197                 if(b[k]){
33198                     buttons[k].show();
33199                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33200                     width += buttons[k].el.getWidth()+15;
33201                 }else{
33202                     buttons[k].hide();
33203                 }
33204             }
33205         }
33206         return width;
33207     };
33208
33209     // private
33210     var handleEsc = function(d, k, e){
33211         if(opt && opt.closable !== false){
33212             dlg.hide();
33213         }
33214         if(e){
33215             e.stopEvent();
33216         }
33217     };
33218
33219     return {
33220         /**
33221          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33222          * @return {Roo.BasicDialog} The BasicDialog element
33223          */
33224         getDialog : function(){
33225            if(!dlg){
33226                 dlg = new Roo.BasicDialog("x-msg-box", {
33227                     autoCreate : true,
33228                     shadow: true,
33229                     draggable: true,
33230                     resizable:false,
33231                     constraintoviewport:false,
33232                     fixedcenter:true,
33233                     collapsible : false,
33234                     shim:true,
33235                     modal: true,
33236                     width:400, height:100,
33237                     buttonAlign:"center",
33238                     closeClick : function(){
33239                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33240                             handleButton("no");
33241                         }else{
33242                             handleButton("cancel");
33243                         }
33244                     }
33245                 });
33246                 dlg.on("hide", handleHide);
33247                 mask = dlg.mask;
33248                 dlg.addKeyListener(27, handleEsc);
33249                 buttons = {};
33250                 var bt = this.buttonText;
33251                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33252                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33253                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33254                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33255                 bodyEl = dlg.body.createChild({
33256
33257                     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>'
33258                 });
33259                 msgEl = bodyEl.dom.firstChild;
33260                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33261                 textboxEl.enableDisplayMode();
33262                 textboxEl.addKeyListener([10,13], function(){
33263                     if(dlg.isVisible() && opt && opt.buttons){
33264                         if(opt.buttons.ok){
33265                             handleButton("ok");
33266                         }else if(opt.buttons.yes){
33267                             handleButton("yes");
33268                         }
33269                     }
33270                 });
33271                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33272                 textareaEl.enableDisplayMode();
33273                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33274                 progressEl.enableDisplayMode();
33275                 var pf = progressEl.dom.firstChild;
33276                 if (pf) {
33277                     pp = Roo.get(pf.firstChild);
33278                     pp.setHeight(pf.offsetHeight);
33279                 }
33280                 
33281             }
33282             return dlg;
33283         },
33284
33285         /**
33286          * Updates the message box body text
33287          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33288          * the XHTML-compliant non-breaking space character '&amp;#160;')
33289          * @return {Roo.MessageBox} This message box
33290          */
33291         updateText : function(text){
33292             if(!dlg.isVisible() && !opt.width){
33293                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33294             }
33295             msgEl.innerHTML = text || '&#160;';
33296       
33297             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33298             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33299             var w = Math.max(
33300                     Math.min(opt.width || cw , this.maxWidth), 
33301                     Math.max(opt.minWidth || this.minWidth, bwidth)
33302             );
33303             if(opt.prompt){
33304                 activeTextEl.setWidth(w);
33305             }
33306             if(dlg.isVisible()){
33307                 dlg.fixedcenter = false;
33308             }
33309             // to big, make it scroll. = But as usual stupid IE does not support
33310             // !important..
33311             
33312             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33313                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33314                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33315             } else {
33316                 bodyEl.dom.style.height = '';
33317                 bodyEl.dom.style.overflowY = '';
33318             }
33319             if (cw > w) {
33320                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33321             } else {
33322                 bodyEl.dom.style.overflowX = '';
33323             }
33324             
33325             dlg.setContentSize(w, bodyEl.getHeight());
33326             if(dlg.isVisible()){
33327                 dlg.fixedcenter = true;
33328             }
33329             return this;
33330         },
33331
33332         /**
33333          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33334          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33335          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33336          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33337          * @return {Roo.MessageBox} This message box
33338          */
33339         updateProgress : function(value, text){
33340             if(text){
33341                 this.updateText(text);
33342             }
33343             if (pp) { // weird bug on my firefox - for some reason this is not defined
33344                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33345             }
33346             return this;
33347         },        
33348
33349         /**
33350          * Returns true if the message box is currently displayed
33351          * @return {Boolean} True if the message box is visible, else false
33352          */
33353         isVisible : function(){
33354             return dlg && dlg.isVisible();  
33355         },
33356
33357         /**
33358          * Hides the message box if it is displayed
33359          */
33360         hide : function(){
33361             if(this.isVisible()){
33362                 dlg.hide();
33363             }  
33364         },
33365
33366         /**
33367          * Displays a new message box, or reinitializes an existing message box, based on the config options
33368          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33369          * The following config object properties are supported:
33370          * <pre>
33371 Property    Type             Description
33372 ----------  ---------------  ------------------------------------------------------------------------------------
33373 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33374                                    closes (defaults to undefined)
33375 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33376                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33377 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33378                                    progress and wait dialogs will ignore this property and always hide the
33379                                    close button as they can only be closed programmatically.
33380 cls               String           A custom CSS class to apply to the message box element
33381 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33382                                    displayed (defaults to 75)
33383 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33384                                    function will be btn (the name of the button that was clicked, if applicable,
33385                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33386                                    Progress and wait dialogs will ignore this option since they do not respond to
33387                                    user actions and can only be closed programmatically, so any required function
33388                                    should be called by the same code after it closes the dialog.
33389 icon              String           A CSS class that provides a background image to be used as an icon for
33390                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33391 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33392 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33393 modal             Boolean          False to allow user interaction with the page while the message box is
33394                                    displayed (defaults to true)
33395 msg               String           A string that will replace the existing message box body text (defaults
33396                                    to the XHTML-compliant non-breaking space character '&#160;')
33397 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33398 progress          Boolean          True to display a progress bar (defaults to false)
33399 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33400 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33401 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33402 title             String           The title text
33403 value             String           The string value to set into the active textbox element if displayed
33404 wait              Boolean          True to display a progress bar (defaults to false)
33405 width             Number           The width of the dialog in pixels
33406 </pre>
33407          *
33408          * Example usage:
33409          * <pre><code>
33410 Roo.Msg.show({
33411    title: 'Address',
33412    msg: 'Please enter your address:',
33413    width: 300,
33414    buttons: Roo.MessageBox.OKCANCEL,
33415    multiline: true,
33416    fn: saveAddress,
33417    animEl: 'addAddressBtn'
33418 });
33419 </code></pre>
33420          * @param {Object} config Configuration options
33421          * @return {Roo.MessageBox} This message box
33422          */
33423         show : function(options)
33424         {
33425             
33426             // this causes nightmares if you show one dialog after another
33427             // especially on callbacks..
33428              
33429             if(this.isVisible()){
33430                 
33431                 this.hide();
33432                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33433                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33434                 Roo.log("New Dialog Message:" +  options.msg )
33435                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33436                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33437                 
33438             }
33439             var d = this.getDialog();
33440             opt = options;
33441             d.setTitle(opt.title || "&#160;");
33442             d.close.setDisplayed(opt.closable !== false);
33443             activeTextEl = textboxEl;
33444             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33445             if(opt.prompt){
33446                 if(opt.multiline){
33447                     textboxEl.hide();
33448                     textareaEl.show();
33449                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33450                         opt.multiline : this.defaultTextHeight);
33451                     activeTextEl = textareaEl;
33452                 }else{
33453                     textboxEl.show();
33454                     textareaEl.hide();
33455                 }
33456             }else{
33457                 textboxEl.hide();
33458                 textareaEl.hide();
33459             }
33460             progressEl.setDisplayed(opt.progress === true);
33461             this.updateProgress(0);
33462             activeTextEl.dom.value = opt.value || "";
33463             if(opt.prompt){
33464                 dlg.setDefaultButton(activeTextEl);
33465             }else{
33466                 var bs = opt.buttons;
33467                 var db = null;
33468                 if(bs && bs.ok){
33469                     db = buttons["ok"];
33470                 }else if(bs && bs.yes){
33471                     db = buttons["yes"];
33472                 }
33473                 dlg.setDefaultButton(db);
33474             }
33475             bwidth = updateButtons(opt.buttons);
33476             this.updateText(opt.msg);
33477             if(opt.cls){
33478                 d.el.addClass(opt.cls);
33479             }
33480             d.proxyDrag = opt.proxyDrag === true;
33481             d.modal = opt.modal !== false;
33482             d.mask = opt.modal !== false ? mask : false;
33483             if(!d.isVisible()){
33484                 // force it to the end of the z-index stack so it gets a cursor in FF
33485                 document.body.appendChild(dlg.el.dom);
33486                 d.animateTarget = null;
33487                 d.show(options.animEl);
33488             }
33489             return this;
33490         },
33491
33492         /**
33493          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33494          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33495          * and closing the message box when the process is complete.
33496          * @param {String} title The title bar text
33497          * @param {String} msg The message box body text
33498          * @return {Roo.MessageBox} This message box
33499          */
33500         progress : function(title, msg){
33501             this.show({
33502                 title : title,
33503                 msg : msg,
33504                 buttons: false,
33505                 progress:true,
33506                 closable:false,
33507                 minWidth: this.minProgressWidth,
33508                 modal : true
33509             });
33510             return this;
33511         },
33512
33513         /**
33514          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33515          * If a callback function is passed it will be called after the user clicks the button, and the
33516          * id of the button that was clicked will be passed as the only parameter to the callback
33517          * (could also be the top-right close button).
33518          * @param {String} title The title bar text
33519          * @param {String} msg The message box body text
33520          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33521          * @param {Object} scope (optional) The scope of the callback function
33522          * @return {Roo.MessageBox} This message box
33523          */
33524         alert : function(title, msg, fn, scope){
33525             this.show({
33526                 title : title,
33527                 msg : msg,
33528                 buttons: this.OK,
33529                 fn: fn,
33530                 scope : scope,
33531                 modal : true
33532             });
33533             return this;
33534         },
33535
33536         /**
33537          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33538          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33539          * You are responsible for closing the message box when the process is complete.
33540          * @param {String} msg The message box body text
33541          * @param {String} title (optional) The title bar text
33542          * @return {Roo.MessageBox} This message box
33543          */
33544         wait : function(msg, title){
33545             this.show({
33546                 title : title,
33547                 msg : msg,
33548                 buttons: false,
33549                 closable:false,
33550                 progress:true,
33551                 modal:true,
33552                 width:300,
33553                 wait:true
33554             });
33555             waitTimer = Roo.TaskMgr.start({
33556                 run: function(i){
33557                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33558                 },
33559                 interval: 1000
33560             });
33561             return this;
33562         },
33563
33564         /**
33565          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33566          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33567          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33568          * @param {String} title The title bar text
33569          * @param {String} msg The message box body text
33570          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33571          * @param {Object} scope (optional) The scope of the callback function
33572          * @return {Roo.MessageBox} This message box
33573          */
33574         confirm : function(title, msg, fn, scope){
33575             this.show({
33576                 title : title,
33577                 msg : msg,
33578                 buttons: this.YESNO,
33579                 fn: fn,
33580                 scope : scope,
33581                 modal : true
33582             });
33583             return this;
33584         },
33585
33586         /**
33587          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33588          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33589          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33590          * (could also be the top-right close button) and the text that was entered will be passed as the two
33591          * parameters to the callback.
33592          * @param {String} title The title bar text
33593          * @param {String} msg The message box body text
33594          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33595          * @param {Object} scope (optional) The scope of the callback function
33596          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33597          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33598          * @return {Roo.MessageBox} This message box
33599          */
33600         prompt : function(title, msg, fn, scope, multiline){
33601             this.show({
33602                 title : title,
33603                 msg : msg,
33604                 buttons: this.OKCANCEL,
33605                 fn: fn,
33606                 minWidth:250,
33607                 scope : scope,
33608                 prompt:true,
33609                 multiline: multiline,
33610                 modal : true
33611             });
33612             return this;
33613         },
33614
33615         /**
33616          * Button config that displays a single OK button
33617          * @type Object
33618          */
33619         OK : {ok:true},
33620         /**
33621          * Button config that displays Yes and No buttons
33622          * @type Object
33623          */
33624         YESNO : {yes:true, no:true},
33625         /**
33626          * Button config that displays OK and Cancel buttons
33627          * @type Object
33628          */
33629         OKCANCEL : {ok:true, cancel:true},
33630         /**
33631          * Button config that displays Yes, No and Cancel buttons
33632          * @type Object
33633          */
33634         YESNOCANCEL : {yes:true, no:true, cancel:true},
33635
33636         /**
33637          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33638          * @type Number
33639          */
33640         defaultTextHeight : 75,
33641         /**
33642          * The maximum width in pixels of the message box (defaults to 600)
33643          * @type Number
33644          */
33645         maxWidth : 600,
33646         /**
33647          * The minimum width in pixels of the message box (defaults to 100)
33648          * @type Number
33649          */
33650         minWidth : 100,
33651         /**
33652          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33653          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33654          * @type Number
33655          */
33656         minProgressWidth : 250,
33657         /**
33658          * An object containing the default button text strings that can be overriden for localized language support.
33659          * Supported properties are: ok, cancel, yes and no.
33660          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33661          * @type Object
33662          */
33663         buttonText : {
33664             ok : "OK",
33665             cancel : "Cancel",
33666             yes : "Yes",
33667             no : "No"
33668         }
33669     };
33670 }();
33671
33672 /**
33673  * Shorthand for {@link Roo.MessageBox}
33674  */
33675 Roo.Msg = Roo.MessageBox;/*
33676  * Based on:
33677  * Ext JS Library 1.1.1
33678  * Copyright(c) 2006-2007, Ext JS, LLC.
33679  *
33680  * Originally Released Under LGPL - original licence link has changed is not relivant.
33681  *
33682  * Fork - LGPL
33683  * <script type="text/javascript">
33684  */
33685 /**
33686  * @class Roo.QuickTips
33687  * Provides attractive and customizable tooltips for any element.
33688  * @singleton
33689  */
33690 Roo.QuickTips = function(){
33691     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33692     var ce, bd, xy, dd;
33693     var visible = false, disabled = true, inited = false;
33694     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33695     
33696     var onOver = function(e){
33697         if(disabled){
33698             return;
33699         }
33700         var t = e.getTarget();
33701         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33702             return;
33703         }
33704         if(ce && t == ce.el){
33705             clearTimeout(hideProc);
33706             return;
33707         }
33708         if(t && tagEls[t.id]){
33709             tagEls[t.id].el = t;
33710             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33711             return;
33712         }
33713         var ttp, et = Roo.fly(t);
33714         var ns = cfg.namespace;
33715         if(tm.interceptTitles && t.title){
33716             ttp = t.title;
33717             t.qtip = ttp;
33718             t.removeAttribute("title");
33719             e.preventDefault();
33720         }else{
33721             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33722         }
33723         if(ttp){
33724             showProc = show.defer(tm.showDelay, tm, [{
33725                 el: t, 
33726                 text: ttp.replace(/\\n/g,'<br/>'),
33727                 width: et.getAttributeNS(ns, cfg.width),
33728                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33729                 title: et.getAttributeNS(ns, cfg.title),
33730                     cls: et.getAttributeNS(ns, cfg.cls)
33731             }]);
33732         }
33733     };
33734     
33735     var onOut = function(e){
33736         clearTimeout(showProc);
33737         var t = e.getTarget();
33738         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33739             hideProc = setTimeout(hide, tm.hideDelay);
33740         }
33741     };
33742     
33743     var onMove = function(e){
33744         if(disabled){
33745             return;
33746         }
33747         xy = e.getXY();
33748         xy[1] += 18;
33749         if(tm.trackMouse && ce){
33750             el.setXY(xy);
33751         }
33752     };
33753     
33754     var onDown = function(e){
33755         clearTimeout(showProc);
33756         clearTimeout(hideProc);
33757         if(!e.within(el)){
33758             if(tm.hideOnClick){
33759                 hide();
33760                 tm.disable();
33761                 tm.enable.defer(100, tm);
33762             }
33763         }
33764     };
33765     
33766     var getPad = function(){
33767         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33768     };
33769
33770     var show = function(o){
33771         if(disabled){
33772             return;
33773         }
33774         clearTimeout(dismissProc);
33775         ce = o;
33776         if(removeCls){ // in case manually hidden
33777             el.removeClass(removeCls);
33778             removeCls = null;
33779         }
33780         if(ce.cls){
33781             el.addClass(ce.cls);
33782             removeCls = ce.cls;
33783         }
33784         if(ce.title){
33785             tipTitle.update(ce.title);
33786             tipTitle.show();
33787         }else{
33788             tipTitle.update('');
33789             tipTitle.hide();
33790         }
33791         el.dom.style.width  = tm.maxWidth+'px';
33792         //tipBody.dom.style.width = '';
33793         tipBodyText.update(o.text);
33794         var p = getPad(), w = ce.width;
33795         if(!w){
33796             var td = tipBodyText.dom;
33797             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33798             if(aw > tm.maxWidth){
33799                 w = tm.maxWidth;
33800             }else if(aw < tm.minWidth){
33801                 w = tm.minWidth;
33802             }else{
33803                 w = aw;
33804             }
33805         }
33806         //tipBody.setWidth(w);
33807         el.setWidth(parseInt(w, 10) + p);
33808         if(ce.autoHide === false){
33809             close.setDisplayed(true);
33810             if(dd){
33811                 dd.unlock();
33812             }
33813         }else{
33814             close.setDisplayed(false);
33815             if(dd){
33816                 dd.lock();
33817             }
33818         }
33819         if(xy){
33820             el.avoidY = xy[1]-18;
33821             el.setXY(xy);
33822         }
33823         if(tm.animate){
33824             el.setOpacity(.1);
33825             el.setStyle("visibility", "visible");
33826             el.fadeIn({callback: afterShow});
33827         }else{
33828             afterShow();
33829         }
33830     };
33831     
33832     var afterShow = function(){
33833         if(ce){
33834             el.show();
33835             esc.enable();
33836             if(tm.autoDismiss && ce.autoHide !== false){
33837                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33838             }
33839         }
33840     };
33841     
33842     var hide = function(noanim){
33843         clearTimeout(dismissProc);
33844         clearTimeout(hideProc);
33845         ce = null;
33846         if(el.isVisible()){
33847             esc.disable();
33848             if(noanim !== true && tm.animate){
33849                 el.fadeOut({callback: afterHide});
33850             }else{
33851                 afterHide();
33852             } 
33853         }
33854     };
33855     
33856     var afterHide = function(){
33857         el.hide();
33858         if(removeCls){
33859             el.removeClass(removeCls);
33860             removeCls = null;
33861         }
33862     };
33863     
33864     return {
33865         /**
33866         * @cfg {Number} minWidth
33867         * The minimum width of the quick tip (defaults to 40)
33868         */
33869        minWidth : 40,
33870         /**
33871         * @cfg {Number} maxWidth
33872         * The maximum width of the quick tip (defaults to 300)
33873         */
33874        maxWidth : 300,
33875         /**
33876         * @cfg {Boolean} interceptTitles
33877         * True to automatically use the element's DOM title value if available (defaults to false)
33878         */
33879        interceptTitles : false,
33880         /**
33881         * @cfg {Boolean} trackMouse
33882         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33883         */
33884        trackMouse : false,
33885         /**
33886         * @cfg {Boolean} hideOnClick
33887         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33888         */
33889        hideOnClick : true,
33890         /**
33891         * @cfg {Number} showDelay
33892         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33893         */
33894        showDelay : 500,
33895         /**
33896         * @cfg {Number} hideDelay
33897         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33898         */
33899        hideDelay : 200,
33900         /**
33901         * @cfg {Boolean} autoHide
33902         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33903         * Used in conjunction with hideDelay.
33904         */
33905        autoHide : true,
33906         /**
33907         * @cfg {Boolean}
33908         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33909         * (defaults to true).  Used in conjunction with autoDismissDelay.
33910         */
33911        autoDismiss : true,
33912         /**
33913         * @cfg {Number}
33914         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33915         */
33916        autoDismissDelay : 5000,
33917        /**
33918         * @cfg {Boolean} animate
33919         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33920         */
33921        animate : false,
33922
33923        /**
33924         * @cfg {String} title
33925         * Title text to display (defaults to '').  This can be any valid HTML markup.
33926         */
33927         title: '',
33928        /**
33929         * @cfg {String} text
33930         * Body text to display (defaults to '').  This can be any valid HTML markup.
33931         */
33932         text : '',
33933        /**
33934         * @cfg {String} cls
33935         * A CSS class to apply to the base quick tip element (defaults to '').
33936         */
33937         cls : '',
33938        /**
33939         * @cfg {Number} width
33940         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33941         * minWidth or maxWidth.
33942         */
33943         width : null,
33944
33945     /**
33946      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33947      * or display QuickTips in a page.
33948      */
33949        init : function(){
33950           tm = Roo.QuickTips;
33951           cfg = tm.tagConfig;
33952           if(!inited){
33953               if(!Roo.isReady){ // allow calling of init() before onReady
33954                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33955                   return;
33956               }
33957               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33958               el.fxDefaults = {stopFx: true};
33959               // maximum custom styling
33960               //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>');
33961               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>');              
33962               tipTitle = el.child('h3');
33963               tipTitle.enableDisplayMode("block");
33964               tipBody = el.child('div.x-tip-bd');
33965               tipBodyText = el.child('div.x-tip-bd-inner');
33966               //bdLeft = el.child('div.x-tip-bd-left');
33967               //bdRight = el.child('div.x-tip-bd-right');
33968               close = el.child('div.x-tip-close');
33969               close.enableDisplayMode("block");
33970               close.on("click", hide);
33971               var d = Roo.get(document);
33972               d.on("mousedown", onDown);
33973               d.on("mouseover", onOver);
33974               d.on("mouseout", onOut);
33975               d.on("mousemove", onMove);
33976               esc = d.addKeyListener(27, hide);
33977               esc.disable();
33978               if(Roo.dd.DD){
33979                   dd = el.initDD("default", null, {
33980                       onDrag : function(){
33981                           el.sync();  
33982                       }
33983                   });
33984                   dd.setHandleElId(tipTitle.id);
33985                   dd.lock();
33986               }
33987               inited = true;
33988           }
33989           this.enable(); 
33990        },
33991
33992     /**
33993      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33994      * are supported:
33995      * <pre>
33996 Property    Type                   Description
33997 ----------  ---------------------  ------------------------------------------------------------------------
33998 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33999      * </ul>
34000      * @param {Object} config The config object
34001      */
34002        register : function(config){
34003            var cs = config instanceof Array ? config : arguments;
34004            for(var i = 0, len = cs.length; i < len; i++) {
34005                var c = cs[i];
34006                var target = c.target;
34007                if(target){
34008                    if(target instanceof Array){
34009                        for(var j = 0, jlen = target.length; j < jlen; j++){
34010                            tagEls[target[j]] = c;
34011                        }
34012                    }else{
34013                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34014                    }
34015                }
34016            }
34017        },
34018
34019     /**
34020      * Removes this quick tip from its element and destroys it.
34021      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34022      */
34023        unregister : function(el){
34024            delete tagEls[Roo.id(el)];
34025        },
34026
34027     /**
34028      * Enable this quick tip.
34029      */
34030        enable : function(){
34031            if(inited && disabled){
34032                locks.pop();
34033                if(locks.length < 1){
34034                    disabled = false;
34035                }
34036            }
34037        },
34038
34039     /**
34040      * Disable this quick tip.
34041      */
34042        disable : function(){
34043           disabled = true;
34044           clearTimeout(showProc);
34045           clearTimeout(hideProc);
34046           clearTimeout(dismissProc);
34047           if(ce){
34048               hide(true);
34049           }
34050           locks.push(1);
34051        },
34052
34053     /**
34054      * Returns true if the quick tip is enabled, else false.
34055      */
34056        isEnabled : function(){
34057             return !disabled;
34058        },
34059
34060         // private
34061        tagConfig : {
34062            namespace : "roo", // was ext?? this may break..
34063            alt_namespace : "ext",
34064            attribute : "qtip",
34065            width : "width",
34066            target : "target",
34067            title : "qtitle",
34068            hide : "hide",
34069            cls : "qclass"
34070        }
34071    };
34072 }();
34073
34074 // backwards compat
34075 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34076  * Based on:
34077  * Ext JS Library 1.1.1
34078  * Copyright(c) 2006-2007, Ext JS, LLC.
34079  *
34080  * Originally Released Under LGPL - original licence link has changed is not relivant.
34081  *
34082  * Fork - LGPL
34083  * <script type="text/javascript">
34084  */
34085  
34086
34087 /**
34088  * @class Roo.tree.TreePanel
34089  * @extends Roo.data.Tree
34090
34091  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34092  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34093  * @cfg {Boolean} enableDD true to enable drag and drop
34094  * @cfg {Boolean} enableDrag true to enable just drag
34095  * @cfg {Boolean} enableDrop true to enable just drop
34096  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34097  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34098  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34099  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34100  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34101  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34102  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34103  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34104  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34105  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34106  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34107  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34108  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34109  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34110  * @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>
34111  * @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>
34112  * 
34113  * @constructor
34114  * @param {String/HTMLElement/Element} el The container element
34115  * @param {Object} config
34116  */
34117 Roo.tree.TreePanel = function(el, config){
34118     var root = false;
34119     var loader = false;
34120     if (config.root) {
34121         root = config.root;
34122         delete config.root;
34123     }
34124     if (config.loader) {
34125         loader = config.loader;
34126         delete config.loader;
34127     }
34128     
34129     Roo.apply(this, config);
34130     Roo.tree.TreePanel.superclass.constructor.call(this);
34131     this.el = Roo.get(el);
34132     this.el.addClass('x-tree');
34133     //console.log(root);
34134     if (root) {
34135         this.setRootNode( Roo.factory(root, Roo.tree));
34136     }
34137     if (loader) {
34138         this.loader = Roo.factory(loader, Roo.tree);
34139     }
34140    /**
34141     * Read-only. The id of the container element becomes this TreePanel's id.
34142     */
34143     this.id = this.el.id;
34144     this.addEvents({
34145         /**
34146         * @event beforeload
34147         * Fires before a node is loaded, return false to cancel
34148         * @param {Node} node The node being loaded
34149         */
34150         "beforeload" : true,
34151         /**
34152         * @event load
34153         * Fires when a node is loaded
34154         * @param {Node} node The node that was loaded
34155         */
34156         "load" : true,
34157         /**
34158         * @event textchange
34159         * Fires when the text for a node is changed
34160         * @param {Node} node The node
34161         * @param {String} text The new text
34162         * @param {String} oldText The old text
34163         */
34164         "textchange" : true,
34165         /**
34166         * @event beforeexpand
34167         * Fires before a node is expanded, return false to cancel.
34168         * @param {Node} node The node
34169         * @param {Boolean} deep
34170         * @param {Boolean} anim
34171         */
34172         "beforeexpand" : true,
34173         /**
34174         * @event beforecollapse
34175         * Fires before a node is collapsed, return false to cancel.
34176         * @param {Node} node The node
34177         * @param {Boolean} deep
34178         * @param {Boolean} anim
34179         */
34180         "beforecollapse" : true,
34181         /**
34182         * @event expand
34183         * Fires when a node is expanded
34184         * @param {Node} node The node
34185         */
34186         "expand" : true,
34187         /**
34188         * @event disabledchange
34189         * Fires when the disabled status of a node changes
34190         * @param {Node} node The node
34191         * @param {Boolean} disabled
34192         */
34193         "disabledchange" : true,
34194         /**
34195         * @event collapse
34196         * Fires when a node is collapsed
34197         * @param {Node} node The node
34198         */
34199         "collapse" : true,
34200         /**
34201         * @event beforeclick
34202         * Fires before click processing on a node. Return false to cancel the default action.
34203         * @param {Node} node The node
34204         * @param {Roo.EventObject} e The event object
34205         */
34206         "beforeclick":true,
34207         /**
34208         * @event checkchange
34209         * Fires when a node with a checkbox's checked property changes
34210         * @param {Node} this This node
34211         * @param {Boolean} checked
34212         */
34213         "checkchange":true,
34214         /**
34215         * @event click
34216         * Fires when a node is clicked
34217         * @param {Node} node The node
34218         * @param {Roo.EventObject} e The event object
34219         */
34220         "click":true,
34221         /**
34222         * @event dblclick
34223         * Fires when a node is double clicked
34224         * @param {Node} node The node
34225         * @param {Roo.EventObject} e The event object
34226         */
34227         "dblclick":true,
34228         /**
34229         * @event contextmenu
34230         * Fires when a node is right clicked
34231         * @param {Node} node The node
34232         * @param {Roo.EventObject} e The event object
34233         */
34234         "contextmenu":true,
34235         /**
34236         * @event beforechildrenrendered
34237         * Fires right before the child nodes for a node are rendered
34238         * @param {Node} node The node
34239         */
34240         "beforechildrenrendered":true,
34241         /**
34242         * @event startdrag
34243         * Fires when a node starts being dragged
34244         * @param {Roo.tree.TreePanel} this
34245         * @param {Roo.tree.TreeNode} node
34246         * @param {event} e The raw browser event
34247         */ 
34248        "startdrag" : true,
34249        /**
34250         * @event enddrag
34251         * Fires when a drag operation is complete
34252         * @param {Roo.tree.TreePanel} this
34253         * @param {Roo.tree.TreeNode} node
34254         * @param {event} e The raw browser event
34255         */
34256        "enddrag" : true,
34257        /**
34258         * @event dragdrop
34259         * Fires when a dragged node is dropped on a valid DD target
34260         * @param {Roo.tree.TreePanel} this
34261         * @param {Roo.tree.TreeNode} node
34262         * @param {DD} dd The dd it was dropped on
34263         * @param {event} e The raw browser event
34264         */
34265        "dragdrop" : true,
34266        /**
34267         * @event beforenodedrop
34268         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34269         * passed to handlers has the following properties:<br />
34270         * <ul style="padding:5px;padding-left:16px;">
34271         * <li>tree - The TreePanel</li>
34272         * <li>target - The node being targeted for the drop</li>
34273         * <li>data - The drag data from the drag source</li>
34274         * <li>point - The point of the drop - append, above or below</li>
34275         * <li>source - The drag source</li>
34276         * <li>rawEvent - Raw mouse event</li>
34277         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34278         * to be inserted by setting them on this object.</li>
34279         * <li>cancel - Set this to true to cancel the drop.</li>
34280         * </ul>
34281         * @param {Object} dropEvent
34282         */
34283        "beforenodedrop" : true,
34284        /**
34285         * @event nodedrop
34286         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34287         * passed to handlers has the following properties:<br />
34288         * <ul style="padding:5px;padding-left:16px;">
34289         * <li>tree - The TreePanel</li>
34290         * <li>target - The node being targeted for the drop</li>
34291         * <li>data - The drag data from the drag source</li>
34292         * <li>point - The point of the drop - append, above or below</li>
34293         * <li>source - The drag source</li>
34294         * <li>rawEvent - Raw mouse event</li>
34295         * <li>dropNode - Dropped node(s).</li>
34296         * </ul>
34297         * @param {Object} dropEvent
34298         */
34299        "nodedrop" : true,
34300         /**
34301         * @event nodedragover
34302         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34303         * passed to handlers has the following properties:<br />
34304         * <ul style="padding:5px;padding-left:16px;">
34305         * <li>tree - The TreePanel</li>
34306         * <li>target - The node being targeted for the drop</li>
34307         * <li>data - The drag data from the drag source</li>
34308         * <li>point - The point of the drop - append, above or below</li>
34309         * <li>source - The drag source</li>
34310         * <li>rawEvent - Raw mouse event</li>
34311         * <li>dropNode - Drop node(s) provided by the source.</li>
34312         * <li>cancel - Set this to true to signal drop not allowed.</li>
34313         * </ul>
34314         * @param {Object} dragOverEvent
34315         */
34316        "nodedragover" : true,
34317        /**
34318         * @event appendnode
34319         * Fires when append node to the tree
34320         * @param {Roo.tree.TreePanel} this
34321         * @param {Roo.tree.TreeNode} node
34322         * @param {Number} index The index of the newly appended node
34323         */
34324        "appendnode" : true
34325         
34326     });
34327     if(this.singleExpand){
34328        this.on("beforeexpand", this.restrictExpand, this);
34329     }
34330     if (this.editor) {
34331         this.editor.tree = this;
34332         this.editor = Roo.factory(this.editor, Roo.tree);
34333     }
34334     
34335     if (this.selModel) {
34336         this.selModel = Roo.factory(this.selModel, Roo.tree);
34337     }
34338    
34339 };
34340 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34341     rootVisible : true,
34342     animate: Roo.enableFx,
34343     lines : true,
34344     enableDD : false,
34345     hlDrop : Roo.enableFx,
34346   
34347     renderer: false,
34348     
34349     rendererTip: false,
34350     // private
34351     restrictExpand : function(node){
34352         var p = node.parentNode;
34353         if(p){
34354             if(p.expandedChild && p.expandedChild.parentNode == p){
34355                 p.expandedChild.collapse();
34356             }
34357             p.expandedChild = node;
34358         }
34359     },
34360
34361     // private override
34362     setRootNode : function(node){
34363         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34364         if(!this.rootVisible){
34365             node.ui = new Roo.tree.RootTreeNodeUI(node);
34366         }
34367         return node;
34368     },
34369
34370     /**
34371      * Returns the container element for this TreePanel
34372      */
34373     getEl : function(){
34374         return this.el;
34375     },
34376
34377     /**
34378      * Returns the default TreeLoader for this TreePanel
34379      */
34380     getLoader : function(){
34381         return this.loader;
34382     },
34383
34384     /**
34385      * Expand all nodes
34386      */
34387     expandAll : function(){
34388         this.root.expand(true);
34389     },
34390
34391     /**
34392      * Collapse all nodes
34393      */
34394     collapseAll : function(){
34395         this.root.collapse(true);
34396     },
34397
34398     /**
34399      * Returns the selection model used by this TreePanel
34400      */
34401     getSelectionModel : function(){
34402         if(!this.selModel){
34403             this.selModel = new Roo.tree.DefaultSelectionModel();
34404         }
34405         return this.selModel;
34406     },
34407
34408     /**
34409      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34410      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34411      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34412      * @return {Array}
34413      */
34414     getChecked : function(a, startNode){
34415         startNode = startNode || this.root;
34416         var r = [];
34417         var f = function(){
34418             if(this.attributes.checked){
34419                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34420             }
34421         }
34422         startNode.cascade(f);
34423         return r;
34424     },
34425
34426     /**
34427      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34428      * @param {String} path
34429      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34430      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34431      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34432      */
34433     expandPath : function(path, attr, callback){
34434         attr = attr || "id";
34435         var keys = path.split(this.pathSeparator);
34436         var curNode = this.root;
34437         if(curNode.attributes[attr] != keys[1]){ // invalid root
34438             if(callback){
34439                 callback(false, null);
34440             }
34441             return;
34442         }
34443         var index = 1;
34444         var f = function(){
34445             if(++index == keys.length){
34446                 if(callback){
34447                     callback(true, curNode);
34448                 }
34449                 return;
34450             }
34451             var c = curNode.findChild(attr, keys[index]);
34452             if(!c){
34453                 if(callback){
34454                     callback(false, curNode);
34455                 }
34456                 return;
34457             }
34458             curNode = c;
34459             c.expand(false, false, f);
34460         };
34461         curNode.expand(false, false, f);
34462     },
34463
34464     /**
34465      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34466      * @param {String} path
34467      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34468      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34469      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34470      */
34471     selectPath : function(path, attr, callback){
34472         attr = attr || "id";
34473         var keys = path.split(this.pathSeparator);
34474         var v = keys.pop();
34475         if(keys.length > 0){
34476             var f = function(success, node){
34477                 if(success && node){
34478                     var n = node.findChild(attr, v);
34479                     if(n){
34480                         n.select();
34481                         if(callback){
34482                             callback(true, n);
34483                         }
34484                     }else if(callback){
34485                         callback(false, n);
34486                     }
34487                 }else{
34488                     if(callback){
34489                         callback(false, n);
34490                     }
34491                 }
34492             };
34493             this.expandPath(keys.join(this.pathSeparator), attr, f);
34494         }else{
34495             this.root.select();
34496             if(callback){
34497                 callback(true, this.root);
34498             }
34499         }
34500     },
34501
34502     getTreeEl : function(){
34503         return this.el;
34504     },
34505
34506     /**
34507      * Trigger rendering of this TreePanel
34508      */
34509     render : function(){
34510         if (this.innerCt) {
34511             return this; // stop it rendering more than once!!
34512         }
34513         
34514         this.innerCt = this.el.createChild({tag:"ul",
34515                cls:"x-tree-root-ct " +
34516                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34517
34518         if(this.containerScroll){
34519             Roo.dd.ScrollManager.register(this.el);
34520         }
34521         if((this.enableDD || this.enableDrop) && !this.dropZone){
34522            /**
34523             * The dropZone used by this tree if drop is enabled
34524             * @type Roo.tree.TreeDropZone
34525             */
34526              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34527                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34528            });
34529         }
34530         if((this.enableDD || this.enableDrag) && !this.dragZone){
34531            /**
34532             * The dragZone used by this tree if drag is enabled
34533             * @type Roo.tree.TreeDragZone
34534             */
34535             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34536                ddGroup: this.ddGroup || "TreeDD",
34537                scroll: this.ddScroll
34538            });
34539         }
34540         this.getSelectionModel().init(this);
34541         if (!this.root) {
34542             Roo.log("ROOT not set in tree");
34543             return this;
34544         }
34545         this.root.render();
34546         if(!this.rootVisible){
34547             this.root.renderChildren();
34548         }
34549         return this;
34550     }
34551 });/*
34552  * Based on:
34553  * Ext JS Library 1.1.1
34554  * Copyright(c) 2006-2007, Ext JS, LLC.
34555  *
34556  * Originally Released Under LGPL - original licence link has changed is not relivant.
34557  *
34558  * Fork - LGPL
34559  * <script type="text/javascript">
34560  */
34561  
34562
34563 /**
34564  * @class Roo.tree.DefaultSelectionModel
34565  * @extends Roo.util.Observable
34566  * The default single selection for a TreePanel.
34567  * @param {Object} cfg Configuration
34568  */
34569 Roo.tree.DefaultSelectionModel = function(cfg){
34570    this.selNode = null;
34571    
34572    
34573    
34574    this.addEvents({
34575        /**
34576         * @event selectionchange
34577         * Fires when the selected node changes
34578         * @param {DefaultSelectionModel} this
34579         * @param {TreeNode} node the new selection
34580         */
34581        "selectionchange" : true,
34582
34583        /**
34584         * @event beforeselect
34585         * Fires before the selected node changes, return false to cancel the change
34586         * @param {DefaultSelectionModel} this
34587         * @param {TreeNode} node the new selection
34588         * @param {TreeNode} node the old selection
34589         */
34590        "beforeselect" : true
34591    });
34592    
34593     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34594 };
34595
34596 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34597     init : function(tree){
34598         this.tree = tree;
34599         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34600         tree.on("click", this.onNodeClick, this);
34601     },
34602     
34603     onNodeClick : function(node, e){
34604         if (e.ctrlKey && this.selNode == node)  {
34605             this.unselect(node);
34606             return;
34607         }
34608         this.select(node);
34609     },
34610     
34611     /**
34612      * Select a node.
34613      * @param {TreeNode} node The node to select
34614      * @return {TreeNode} The selected node
34615      */
34616     select : function(node){
34617         var last = this.selNode;
34618         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34619             if(last){
34620                 last.ui.onSelectedChange(false);
34621             }
34622             this.selNode = node;
34623             node.ui.onSelectedChange(true);
34624             this.fireEvent("selectionchange", this, node, last);
34625         }
34626         return node;
34627     },
34628     
34629     /**
34630      * Deselect a node.
34631      * @param {TreeNode} node The node to unselect
34632      */
34633     unselect : function(node){
34634         if(this.selNode == node){
34635             this.clearSelections();
34636         }    
34637     },
34638     
34639     /**
34640      * Clear all selections
34641      */
34642     clearSelections : function(){
34643         var n = this.selNode;
34644         if(n){
34645             n.ui.onSelectedChange(false);
34646             this.selNode = null;
34647             this.fireEvent("selectionchange", this, null);
34648         }
34649         return n;
34650     },
34651     
34652     /**
34653      * Get the selected node
34654      * @return {TreeNode} The selected node
34655      */
34656     getSelectedNode : function(){
34657         return this.selNode;    
34658     },
34659     
34660     /**
34661      * Returns true if the node is selected
34662      * @param {TreeNode} node The node to check
34663      * @return {Boolean}
34664      */
34665     isSelected : function(node){
34666         return this.selNode == node;  
34667     },
34668
34669     /**
34670      * Selects the node above the selected node in the tree, intelligently walking the nodes
34671      * @return TreeNode The new selection
34672      */
34673     selectPrevious : function(){
34674         var s = this.selNode || this.lastSelNode;
34675         if(!s){
34676             return null;
34677         }
34678         var ps = s.previousSibling;
34679         if(ps){
34680             if(!ps.isExpanded() || ps.childNodes.length < 1){
34681                 return this.select(ps);
34682             } else{
34683                 var lc = ps.lastChild;
34684                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34685                     lc = lc.lastChild;
34686                 }
34687                 return this.select(lc);
34688             }
34689         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34690             return this.select(s.parentNode);
34691         }
34692         return null;
34693     },
34694
34695     /**
34696      * Selects the node above the selected node in the tree, intelligently walking the nodes
34697      * @return TreeNode The new selection
34698      */
34699     selectNext : function(){
34700         var s = this.selNode || this.lastSelNode;
34701         if(!s){
34702             return null;
34703         }
34704         if(s.firstChild && s.isExpanded()){
34705              return this.select(s.firstChild);
34706          }else if(s.nextSibling){
34707              return this.select(s.nextSibling);
34708          }else if(s.parentNode){
34709             var newS = null;
34710             s.parentNode.bubble(function(){
34711                 if(this.nextSibling){
34712                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34713                     return false;
34714                 }
34715             });
34716             return newS;
34717          }
34718         return null;
34719     },
34720
34721     onKeyDown : function(e){
34722         var s = this.selNode || this.lastSelNode;
34723         // undesirable, but required
34724         var sm = this;
34725         if(!s){
34726             return;
34727         }
34728         var k = e.getKey();
34729         switch(k){
34730              case e.DOWN:
34731                  e.stopEvent();
34732                  this.selectNext();
34733              break;
34734              case e.UP:
34735                  e.stopEvent();
34736                  this.selectPrevious();
34737              break;
34738              case e.RIGHT:
34739                  e.preventDefault();
34740                  if(s.hasChildNodes()){
34741                      if(!s.isExpanded()){
34742                          s.expand();
34743                      }else if(s.firstChild){
34744                          this.select(s.firstChild, e);
34745                      }
34746                  }
34747              break;
34748              case e.LEFT:
34749                  e.preventDefault();
34750                  if(s.hasChildNodes() && s.isExpanded()){
34751                      s.collapse();
34752                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34753                      this.select(s.parentNode, e);
34754                  }
34755              break;
34756         };
34757     }
34758 });
34759
34760 /**
34761  * @class Roo.tree.MultiSelectionModel
34762  * @extends Roo.util.Observable
34763  * Multi selection for a TreePanel.
34764  * @param {Object} cfg Configuration
34765  */
34766 Roo.tree.MultiSelectionModel = function(){
34767    this.selNodes = [];
34768    this.selMap = {};
34769    this.addEvents({
34770        /**
34771         * @event selectionchange
34772         * Fires when the selected nodes change
34773         * @param {MultiSelectionModel} this
34774         * @param {Array} nodes Array of the selected nodes
34775         */
34776        "selectionchange" : true
34777    });
34778    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34779    
34780 };
34781
34782 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34783     init : function(tree){
34784         this.tree = tree;
34785         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34786         tree.on("click", this.onNodeClick, this);
34787     },
34788     
34789     onNodeClick : function(node, e){
34790         this.select(node, e, e.ctrlKey);
34791     },
34792     
34793     /**
34794      * Select a node.
34795      * @param {TreeNode} node The node to select
34796      * @param {EventObject} e (optional) An event associated with the selection
34797      * @param {Boolean} keepExisting True to retain existing selections
34798      * @return {TreeNode} The selected node
34799      */
34800     select : function(node, e, keepExisting){
34801         if(keepExisting !== true){
34802             this.clearSelections(true);
34803         }
34804         if(this.isSelected(node)){
34805             this.lastSelNode = node;
34806             return node;
34807         }
34808         this.selNodes.push(node);
34809         this.selMap[node.id] = node;
34810         this.lastSelNode = node;
34811         node.ui.onSelectedChange(true);
34812         this.fireEvent("selectionchange", this, this.selNodes);
34813         return node;
34814     },
34815     
34816     /**
34817      * Deselect a node.
34818      * @param {TreeNode} node The node to unselect
34819      */
34820     unselect : function(node){
34821         if(this.selMap[node.id]){
34822             node.ui.onSelectedChange(false);
34823             var sn = this.selNodes;
34824             var index = -1;
34825             if(sn.indexOf){
34826                 index = sn.indexOf(node);
34827             }else{
34828                 for(var i = 0, len = sn.length; i < len; i++){
34829                     if(sn[i] == node){
34830                         index = i;
34831                         break;
34832                     }
34833                 }
34834             }
34835             if(index != -1){
34836                 this.selNodes.splice(index, 1);
34837             }
34838             delete this.selMap[node.id];
34839             this.fireEvent("selectionchange", this, this.selNodes);
34840         }
34841     },
34842     
34843     /**
34844      * Clear all selections
34845      */
34846     clearSelections : function(suppressEvent){
34847         var sn = this.selNodes;
34848         if(sn.length > 0){
34849             for(var i = 0, len = sn.length; i < len; i++){
34850                 sn[i].ui.onSelectedChange(false);
34851             }
34852             this.selNodes = [];
34853             this.selMap = {};
34854             if(suppressEvent !== true){
34855                 this.fireEvent("selectionchange", this, this.selNodes);
34856             }
34857         }
34858     },
34859     
34860     /**
34861      * Returns true if the node is selected
34862      * @param {TreeNode} node The node to check
34863      * @return {Boolean}
34864      */
34865     isSelected : function(node){
34866         return this.selMap[node.id] ? true : false;  
34867     },
34868     
34869     /**
34870      * Returns an array of the selected nodes
34871      * @return {Array}
34872      */
34873     getSelectedNodes : function(){
34874         return this.selNodes;    
34875     },
34876
34877     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34878
34879     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34880
34881     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34882 });/*
34883  * Based on:
34884  * Ext JS Library 1.1.1
34885  * Copyright(c) 2006-2007, Ext JS, LLC.
34886  *
34887  * Originally Released Under LGPL - original licence link has changed is not relivant.
34888  *
34889  * Fork - LGPL
34890  * <script type="text/javascript">
34891  */
34892  
34893 /**
34894  * @class Roo.tree.TreeNode
34895  * @extends Roo.data.Node
34896  * @cfg {String} text The text for this node
34897  * @cfg {Boolean} expanded true to start the node expanded
34898  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34899  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34900  * @cfg {Boolean} disabled true to start the node disabled
34901  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34902  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
34903  * @cfg {String} cls A css class to be added to the node
34904  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34905  * @cfg {String} href URL of the link used for the node (defaults to #)
34906  * @cfg {String} hrefTarget target frame for the link
34907  * @cfg {String} qtip An Ext QuickTip for the node
34908  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34909  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34910  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34911  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34912  * (defaults to undefined with no checkbox rendered)
34913  * @constructor
34914  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34915  */
34916 Roo.tree.TreeNode = function(attributes){
34917     attributes = attributes || {};
34918     if(typeof attributes == "string"){
34919         attributes = {text: attributes};
34920     }
34921     this.childrenRendered = false;
34922     this.rendered = false;
34923     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34924     this.expanded = attributes.expanded === true;
34925     this.isTarget = attributes.isTarget !== false;
34926     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34927     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34928
34929     /**
34930      * Read-only. The text for this node. To change it use setText().
34931      * @type String
34932      */
34933     this.text = attributes.text;
34934     /**
34935      * True if this node is disabled.
34936      * @type Boolean
34937      */
34938     this.disabled = attributes.disabled === true;
34939
34940     this.addEvents({
34941         /**
34942         * @event textchange
34943         * Fires when the text for this node is changed
34944         * @param {Node} this This node
34945         * @param {String} text The new text
34946         * @param {String} oldText The old text
34947         */
34948         "textchange" : true,
34949         /**
34950         * @event beforeexpand
34951         * Fires before this node is expanded, return false to cancel.
34952         * @param {Node} this This node
34953         * @param {Boolean} deep
34954         * @param {Boolean} anim
34955         */
34956         "beforeexpand" : true,
34957         /**
34958         * @event beforecollapse
34959         * Fires before this node is collapsed, return false to cancel.
34960         * @param {Node} this This node
34961         * @param {Boolean} deep
34962         * @param {Boolean} anim
34963         */
34964         "beforecollapse" : true,
34965         /**
34966         * @event expand
34967         * Fires when this node is expanded
34968         * @param {Node} this This node
34969         */
34970         "expand" : true,
34971         /**
34972         * @event disabledchange
34973         * Fires when the disabled status of this node changes
34974         * @param {Node} this This node
34975         * @param {Boolean} disabled
34976         */
34977         "disabledchange" : true,
34978         /**
34979         * @event collapse
34980         * Fires when this node is collapsed
34981         * @param {Node} this This node
34982         */
34983         "collapse" : true,
34984         /**
34985         * @event beforeclick
34986         * Fires before click processing. Return false to cancel the default action.
34987         * @param {Node} this This node
34988         * @param {Roo.EventObject} e The event object
34989         */
34990         "beforeclick":true,
34991         /**
34992         * @event checkchange
34993         * Fires when a node with a checkbox's checked property changes
34994         * @param {Node} this This node
34995         * @param {Boolean} checked
34996         */
34997         "checkchange":true,
34998         /**
34999         * @event click
35000         * Fires when this node is clicked
35001         * @param {Node} this This node
35002         * @param {Roo.EventObject} e The event object
35003         */
35004         "click":true,
35005         /**
35006         * @event dblclick
35007         * Fires when this node is double clicked
35008         * @param {Node} this This node
35009         * @param {Roo.EventObject} e The event object
35010         */
35011         "dblclick":true,
35012         /**
35013         * @event contextmenu
35014         * Fires when this node is right clicked
35015         * @param {Node} this This node
35016         * @param {Roo.EventObject} e The event object
35017         */
35018         "contextmenu":true,
35019         /**
35020         * @event beforechildrenrendered
35021         * Fires right before the child nodes for this node are rendered
35022         * @param {Node} this This node
35023         */
35024         "beforechildrenrendered":true
35025     });
35026
35027     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35028
35029     /**
35030      * Read-only. The UI for this node
35031      * @type TreeNodeUI
35032      */
35033     this.ui = new uiClass(this);
35034     
35035     // finally support items[]
35036     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35037         return;
35038     }
35039     
35040     
35041     Roo.each(this.attributes.items, function(c) {
35042         this.appendChild(Roo.factory(c,Roo.Tree));
35043     }, this);
35044     delete this.attributes.items;
35045     
35046     
35047     
35048 };
35049 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35050     preventHScroll: true,
35051     /**
35052      * Returns true if this node is expanded
35053      * @return {Boolean}
35054      */
35055     isExpanded : function(){
35056         return this.expanded;
35057     },
35058
35059     /**
35060      * Returns the UI object for this node
35061      * @return {TreeNodeUI}
35062      */
35063     getUI : function(){
35064         return this.ui;
35065     },
35066
35067     // private override
35068     setFirstChild : function(node){
35069         var of = this.firstChild;
35070         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35071         if(this.childrenRendered && of && node != of){
35072             of.renderIndent(true, true);
35073         }
35074         if(this.rendered){
35075             this.renderIndent(true, true);
35076         }
35077     },
35078
35079     // private override
35080     setLastChild : function(node){
35081         var ol = this.lastChild;
35082         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35083         if(this.childrenRendered && ol && node != ol){
35084             ol.renderIndent(true, true);
35085         }
35086         if(this.rendered){
35087             this.renderIndent(true, true);
35088         }
35089     },
35090
35091     // these methods are overridden to provide lazy rendering support
35092     // private override
35093     appendChild : function()
35094     {
35095         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35096         if(node && this.childrenRendered){
35097             node.render();
35098         }
35099         this.ui.updateExpandIcon();
35100         return node;
35101     },
35102
35103     // private override
35104     removeChild : function(node){
35105         this.ownerTree.getSelectionModel().unselect(node);
35106         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35107         // if it's been rendered remove dom node
35108         if(this.childrenRendered){
35109             node.ui.remove();
35110         }
35111         if(this.childNodes.length < 1){
35112             this.collapse(false, false);
35113         }else{
35114             this.ui.updateExpandIcon();
35115         }
35116         if(!this.firstChild) {
35117             this.childrenRendered = false;
35118         }
35119         return node;
35120     },
35121
35122     // private override
35123     insertBefore : function(node, refNode){
35124         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35125         if(newNode && refNode && this.childrenRendered){
35126             node.render();
35127         }
35128         this.ui.updateExpandIcon();
35129         return newNode;
35130     },
35131
35132     /**
35133      * Sets the text for this node
35134      * @param {String} text
35135      */
35136     setText : function(text){
35137         var oldText = this.text;
35138         this.text = text;
35139         this.attributes.text = text;
35140         if(this.rendered){ // event without subscribing
35141             this.ui.onTextChange(this, text, oldText);
35142         }
35143         this.fireEvent("textchange", this, text, oldText);
35144     },
35145
35146     /**
35147      * Triggers selection of this node
35148      */
35149     select : function(){
35150         this.getOwnerTree().getSelectionModel().select(this);
35151     },
35152
35153     /**
35154      * Triggers deselection of this node
35155      */
35156     unselect : function(){
35157         this.getOwnerTree().getSelectionModel().unselect(this);
35158     },
35159
35160     /**
35161      * Returns true if this node is selected
35162      * @return {Boolean}
35163      */
35164     isSelected : function(){
35165         return this.getOwnerTree().getSelectionModel().isSelected(this);
35166     },
35167
35168     /**
35169      * Expand this node.
35170      * @param {Boolean} deep (optional) True to expand all children as well
35171      * @param {Boolean} anim (optional) false to cancel the default animation
35172      * @param {Function} callback (optional) A callback to be called when
35173      * expanding this node completes (does not wait for deep expand to complete).
35174      * Called with 1 parameter, this node.
35175      */
35176     expand : function(deep, anim, callback){
35177         if(!this.expanded){
35178             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35179                 return;
35180             }
35181             if(!this.childrenRendered){
35182                 this.renderChildren();
35183             }
35184             this.expanded = true;
35185             
35186             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35187                 this.ui.animExpand(function(){
35188                     this.fireEvent("expand", this);
35189                     if(typeof callback == "function"){
35190                         callback(this);
35191                     }
35192                     if(deep === true){
35193                         this.expandChildNodes(true);
35194                     }
35195                 }.createDelegate(this));
35196                 return;
35197             }else{
35198                 this.ui.expand();
35199                 this.fireEvent("expand", this);
35200                 if(typeof callback == "function"){
35201                     callback(this);
35202                 }
35203             }
35204         }else{
35205            if(typeof callback == "function"){
35206                callback(this);
35207            }
35208         }
35209         if(deep === true){
35210             this.expandChildNodes(true);
35211         }
35212     },
35213
35214     isHiddenRoot : function(){
35215         return this.isRoot && !this.getOwnerTree().rootVisible;
35216     },
35217
35218     /**
35219      * Collapse this node.
35220      * @param {Boolean} deep (optional) True to collapse all children as well
35221      * @param {Boolean} anim (optional) false to cancel the default animation
35222      */
35223     collapse : function(deep, anim){
35224         if(this.expanded && !this.isHiddenRoot()){
35225             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35226                 return;
35227             }
35228             this.expanded = false;
35229             if((this.getOwnerTree().animate && anim !== false) || anim){
35230                 this.ui.animCollapse(function(){
35231                     this.fireEvent("collapse", this);
35232                     if(deep === true){
35233                         this.collapseChildNodes(true);
35234                     }
35235                 }.createDelegate(this));
35236                 return;
35237             }else{
35238                 this.ui.collapse();
35239                 this.fireEvent("collapse", this);
35240             }
35241         }
35242         if(deep === true){
35243             var cs = this.childNodes;
35244             for(var i = 0, len = cs.length; i < len; i++) {
35245                 cs[i].collapse(true, false);
35246             }
35247         }
35248     },
35249
35250     // private
35251     delayedExpand : function(delay){
35252         if(!this.expandProcId){
35253             this.expandProcId = this.expand.defer(delay, this);
35254         }
35255     },
35256
35257     // private
35258     cancelExpand : function(){
35259         if(this.expandProcId){
35260             clearTimeout(this.expandProcId);
35261         }
35262         this.expandProcId = false;
35263     },
35264
35265     /**
35266      * Toggles expanded/collapsed state of the node
35267      */
35268     toggle : function(){
35269         if(this.expanded){
35270             this.collapse();
35271         }else{
35272             this.expand();
35273         }
35274     },
35275
35276     /**
35277      * Ensures all parent nodes are expanded
35278      */
35279     ensureVisible : function(callback){
35280         var tree = this.getOwnerTree();
35281         tree.expandPath(this.parentNode.getPath(), false, function(){
35282             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35283             Roo.callback(callback);
35284         }.createDelegate(this));
35285     },
35286
35287     /**
35288      * Expand all child nodes
35289      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35290      */
35291     expandChildNodes : function(deep){
35292         var cs = this.childNodes;
35293         for(var i = 0, len = cs.length; i < len; i++) {
35294                 cs[i].expand(deep);
35295         }
35296     },
35297
35298     /**
35299      * Collapse all child nodes
35300      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35301      */
35302     collapseChildNodes : function(deep){
35303         var cs = this.childNodes;
35304         for(var i = 0, len = cs.length; i < len; i++) {
35305                 cs[i].collapse(deep);
35306         }
35307     },
35308
35309     /**
35310      * Disables this node
35311      */
35312     disable : function(){
35313         this.disabled = true;
35314         this.unselect();
35315         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35316             this.ui.onDisableChange(this, true);
35317         }
35318         this.fireEvent("disabledchange", this, true);
35319     },
35320
35321     /**
35322      * Enables this node
35323      */
35324     enable : function(){
35325         this.disabled = false;
35326         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35327             this.ui.onDisableChange(this, false);
35328         }
35329         this.fireEvent("disabledchange", this, false);
35330     },
35331
35332     // private
35333     renderChildren : function(suppressEvent){
35334         if(suppressEvent !== false){
35335             this.fireEvent("beforechildrenrendered", this);
35336         }
35337         var cs = this.childNodes;
35338         for(var i = 0, len = cs.length; i < len; i++){
35339             cs[i].render(true);
35340         }
35341         this.childrenRendered = true;
35342     },
35343
35344     // private
35345     sort : function(fn, scope){
35346         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35347         if(this.childrenRendered){
35348             var cs = this.childNodes;
35349             for(var i = 0, len = cs.length; i < len; i++){
35350                 cs[i].render(true);
35351             }
35352         }
35353     },
35354
35355     // private
35356     render : function(bulkRender){
35357         this.ui.render(bulkRender);
35358         if(!this.rendered){
35359             this.rendered = true;
35360             if(this.expanded){
35361                 this.expanded = false;
35362                 this.expand(false, false);
35363             }
35364         }
35365     },
35366
35367     // private
35368     renderIndent : function(deep, refresh){
35369         if(refresh){
35370             this.ui.childIndent = null;
35371         }
35372         this.ui.renderIndent();
35373         if(deep === true && this.childrenRendered){
35374             var cs = this.childNodes;
35375             for(var i = 0, len = cs.length; i < len; i++){
35376                 cs[i].renderIndent(true, refresh);
35377             }
35378         }
35379     }
35380 });/*
35381  * Based on:
35382  * Ext JS Library 1.1.1
35383  * Copyright(c) 2006-2007, Ext JS, LLC.
35384  *
35385  * Originally Released Under LGPL - original licence link has changed is not relivant.
35386  *
35387  * Fork - LGPL
35388  * <script type="text/javascript">
35389  */
35390  
35391 /**
35392  * @class Roo.tree.AsyncTreeNode
35393  * @extends Roo.tree.TreeNode
35394  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35395  * @constructor
35396  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35397  */
35398  Roo.tree.AsyncTreeNode = function(config){
35399     this.loaded = false;
35400     this.loading = false;
35401     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35402     /**
35403     * @event beforeload
35404     * Fires before this node is loaded, return false to cancel
35405     * @param {Node} this This node
35406     */
35407     this.addEvents({'beforeload':true, 'load': true});
35408     /**
35409     * @event load
35410     * Fires when this node is loaded
35411     * @param {Node} this This node
35412     */
35413     /**
35414      * The loader used by this node (defaults to using the tree's defined loader)
35415      * @type TreeLoader
35416      * @property loader
35417      */
35418 };
35419 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35420     expand : function(deep, anim, callback){
35421         if(this.loading){ // if an async load is already running, waiting til it's done
35422             var timer;
35423             var f = function(){
35424                 if(!this.loading){ // done loading
35425                     clearInterval(timer);
35426                     this.expand(deep, anim, callback);
35427                 }
35428             }.createDelegate(this);
35429             timer = setInterval(f, 200);
35430             return;
35431         }
35432         if(!this.loaded){
35433             if(this.fireEvent("beforeload", this) === false){
35434                 return;
35435             }
35436             this.loading = true;
35437             this.ui.beforeLoad(this);
35438             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35439             if(loader){
35440                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35441                 return;
35442             }
35443         }
35444         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35445     },
35446     
35447     /**
35448      * Returns true if this node is currently loading
35449      * @return {Boolean}
35450      */
35451     isLoading : function(){
35452         return this.loading;  
35453     },
35454     
35455     loadComplete : function(deep, anim, callback){
35456         this.loading = false;
35457         this.loaded = true;
35458         this.ui.afterLoad(this);
35459         this.fireEvent("load", this);
35460         this.expand(deep, anim, callback);
35461     },
35462     
35463     /**
35464      * Returns true if this node has been loaded
35465      * @return {Boolean}
35466      */
35467     isLoaded : function(){
35468         return this.loaded;
35469     },
35470     
35471     hasChildNodes : function(){
35472         if(!this.isLeaf() && !this.loaded){
35473             return true;
35474         }else{
35475             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35476         }
35477     },
35478
35479     /**
35480      * Trigger a reload for this node
35481      * @param {Function} callback
35482      */
35483     reload : function(callback){
35484         this.collapse(false, false);
35485         while(this.firstChild){
35486             this.removeChild(this.firstChild);
35487         }
35488         this.childrenRendered = false;
35489         this.loaded = false;
35490         if(this.isHiddenRoot()){
35491             this.expanded = false;
35492         }
35493         this.expand(false, false, callback);
35494     }
35495 });/*
35496  * Based on:
35497  * Ext JS Library 1.1.1
35498  * Copyright(c) 2006-2007, Ext JS, LLC.
35499  *
35500  * Originally Released Under LGPL - original licence link has changed is not relivant.
35501  *
35502  * Fork - LGPL
35503  * <script type="text/javascript">
35504  */
35505  
35506 /**
35507  * @class Roo.tree.TreeNodeUI
35508  * @constructor
35509  * @param {Object} node The node to render
35510  * The TreeNode UI implementation is separate from the
35511  * tree implementation. Unless you are customizing the tree UI,
35512  * you should never have to use this directly.
35513  */
35514 Roo.tree.TreeNodeUI = function(node){
35515     this.node = node;
35516     this.rendered = false;
35517     this.animating = false;
35518     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35519 };
35520
35521 Roo.tree.TreeNodeUI.prototype = {
35522     removeChild : function(node){
35523         if(this.rendered){
35524             this.ctNode.removeChild(node.ui.getEl());
35525         }
35526     },
35527
35528     beforeLoad : function(){
35529          this.addClass("x-tree-node-loading");
35530     },
35531
35532     afterLoad : function(){
35533          this.removeClass("x-tree-node-loading");
35534     },
35535
35536     onTextChange : function(node, text, oldText){
35537         if(this.rendered){
35538             this.textNode.innerHTML = text;
35539         }
35540     },
35541
35542     onDisableChange : function(node, state){
35543         this.disabled = state;
35544         if(state){
35545             this.addClass("x-tree-node-disabled");
35546         }else{
35547             this.removeClass("x-tree-node-disabled");
35548         }
35549     },
35550
35551     onSelectedChange : function(state){
35552         if(state){
35553             this.focus();
35554             this.addClass("x-tree-selected");
35555         }else{
35556             //this.blur();
35557             this.removeClass("x-tree-selected");
35558         }
35559     },
35560
35561     onMove : function(tree, node, oldParent, newParent, index, refNode){
35562         this.childIndent = null;
35563         if(this.rendered){
35564             var targetNode = newParent.ui.getContainer();
35565             if(!targetNode){//target not rendered
35566                 this.holder = document.createElement("div");
35567                 this.holder.appendChild(this.wrap);
35568                 return;
35569             }
35570             var insertBefore = refNode ? refNode.ui.getEl() : null;
35571             if(insertBefore){
35572                 targetNode.insertBefore(this.wrap, insertBefore);
35573             }else{
35574                 targetNode.appendChild(this.wrap);
35575             }
35576             this.node.renderIndent(true);
35577         }
35578     },
35579
35580     addClass : function(cls){
35581         if(this.elNode){
35582             Roo.fly(this.elNode).addClass(cls);
35583         }
35584     },
35585
35586     removeClass : function(cls){
35587         if(this.elNode){
35588             Roo.fly(this.elNode).removeClass(cls);
35589         }
35590     },
35591
35592     remove : function(){
35593         if(this.rendered){
35594             this.holder = document.createElement("div");
35595             this.holder.appendChild(this.wrap);
35596         }
35597     },
35598
35599     fireEvent : function(){
35600         return this.node.fireEvent.apply(this.node, arguments);
35601     },
35602
35603     initEvents : function(){
35604         this.node.on("move", this.onMove, this);
35605         var E = Roo.EventManager;
35606         var a = this.anchor;
35607
35608         var el = Roo.fly(a, '_treeui');
35609
35610         if(Roo.isOpera){ // opera render bug ignores the CSS
35611             el.setStyle("text-decoration", "none");
35612         }
35613
35614         el.on("click", this.onClick, this);
35615         el.on("dblclick", this.onDblClick, this);
35616
35617         if(this.checkbox){
35618             Roo.EventManager.on(this.checkbox,
35619                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35620         }
35621
35622         el.on("contextmenu", this.onContextMenu, this);
35623
35624         var icon = Roo.fly(this.iconNode);
35625         icon.on("click", this.onClick, this);
35626         icon.on("dblclick", this.onDblClick, this);
35627         icon.on("contextmenu", this.onContextMenu, this);
35628         E.on(this.ecNode, "click", this.ecClick, this, true);
35629
35630         if(this.node.disabled){
35631             this.addClass("x-tree-node-disabled");
35632         }
35633         if(this.node.hidden){
35634             this.addClass("x-tree-node-disabled");
35635         }
35636         var ot = this.node.getOwnerTree();
35637         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35638         if(dd && (!this.node.isRoot || ot.rootVisible)){
35639             Roo.dd.Registry.register(this.elNode, {
35640                 node: this.node,
35641                 handles: this.getDDHandles(),
35642                 isHandle: false
35643             });
35644         }
35645     },
35646
35647     getDDHandles : function(){
35648         return [this.iconNode, this.textNode];
35649     },
35650
35651     hide : function(){
35652         if(this.rendered){
35653             this.wrap.style.display = "none";
35654         }
35655     },
35656
35657     show : function(){
35658         if(this.rendered){
35659             this.wrap.style.display = "";
35660         }
35661     },
35662
35663     onContextMenu : function(e){
35664         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35665             e.preventDefault();
35666             this.focus();
35667             this.fireEvent("contextmenu", this.node, e);
35668         }
35669     },
35670
35671     onClick : function(e){
35672         if(this.dropping){
35673             e.stopEvent();
35674             return;
35675         }
35676         if(this.fireEvent("beforeclick", this.node, e) !== false){
35677             if(!this.disabled && this.node.attributes.href){
35678                 this.fireEvent("click", this.node, e);
35679                 return;
35680             }
35681             e.preventDefault();
35682             if(this.disabled){
35683                 return;
35684             }
35685
35686             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35687                 this.node.toggle();
35688             }
35689
35690             this.fireEvent("click", this.node, e);
35691         }else{
35692             e.stopEvent();
35693         }
35694     },
35695
35696     onDblClick : function(e){
35697         e.preventDefault();
35698         if(this.disabled){
35699             return;
35700         }
35701         if(this.checkbox){
35702             this.toggleCheck();
35703         }
35704         if(!this.animating && this.node.hasChildNodes()){
35705             this.node.toggle();
35706         }
35707         this.fireEvent("dblclick", this.node, e);
35708     },
35709
35710     onCheckChange : function(){
35711         var checked = this.checkbox.checked;
35712         this.node.attributes.checked = checked;
35713         this.fireEvent('checkchange', this.node, checked);
35714     },
35715
35716     ecClick : function(e){
35717         if(!this.animating && this.node.hasChildNodes()){
35718             this.node.toggle();
35719         }
35720     },
35721
35722     startDrop : function(){
35723         this.dropping = true;
35724     },
35725
35726     // delayed drop so the click event doesn't get fired on a drop
35727     endDrop : function(){
35728        setTimeout(function(){
35729            this.dropping = false;
35730        }.createDelegate(this), 50);
35731     },
35732
35733     expand : function(){
35734         this.updateExpandIcon();
35735         this.ctNode.style.display = "";
35736     },
35737
35738     focus : function(){
35739         if(!this.node.preventHScroll){
35740             try{this.anchor.focus();
35741             }catch(e){}
35742         }else if(!Roo.isIE){
35743             try{
35744                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35745                 var l = noscroll.scrollLeft;
35746                 this.anchor.focus();
35747                 noscroll.scrollLeft = l;
35748             }catch(e){}
35749         }
35750     },
35751
35752     toggleCheck : function(value){
35753         var cb = this.checkbox;
35754         if(cb){
35755             cb.checked = (value === undefined ? !cb.checked : value);
35756         }
35757     },
35758
35759     blur : function(){
35760         try{
35761             this.anchor.blur();
35762         }catch(e){}
35763     },
35764
35765     animExpand : function(callback){
35766         var ct = Roo.get(this.ctNode);
35767         ct.stopFx();
35768         if(!this.node.hasChildNodes()){
35769             this.updateExpandIcon();
35770             this.ctNode.style.display = "";
35771             Roo.callback(callback);
35772             return;
35773         }
35774         this.animating = true;
35775         this.updateExpandIcon();
35776
35777         ct.slideIn('t', {
35778            callback : function(){
35779                this.animating = false;
35780                Roo.callback(callback);
35781             },
35782             scope: this,
35783             duration: this.node.ownerTree.duration || .25
35784         });
35785     },
35786
35787     highlight : function(){
35788         var tree = this.node.getOwnerTree();
35789         Roo.fly(this.wrap).highlight(
35790             tree.hlColor || "C3DAF9",
35791             {endColor: tree.hlBaseColor}
35792         );
35793     },
35794
35795     collapse : function(){
35796         this.updateExpandIcon();
35797         this.ctNode.style.display = "none";
35798     },
35799
35800     animCollapse : function(callback){
35801         var ct = Roo.get(this.ctNode);
35802         ct.enableDisplayMode('block');
35803         ct.stopFx();
35804
35805         this.animating = true;
35806         this.updateExpandIcon();
35807
35808         ct.slideOut('t', {
35809             callback : function(){
35810                this.animating = false;
35811                Roo.callback(callback);
35812             },
35813             scope: this,
35814             duration: this.node.ownerTree.duration || .25
35815         });
35816     },
35817
35818     getContainer : function(){
35819         return this.ctNode;
35820     },
35821
35822     getEl : function(){
35823         return this.wrap;
35824     },
35825
35826     appendDDGhost : function(ghostNode){
35827         ghostNode.appendChild(this.elNode.cloneNode(true));
35828     },
35829
35830     getDDRepairXY : function(){
35831         return Roo.lib.Dom.getXY(this.iconNode);
35832     },
35833
35834     onRender : function(){
35835         this.render();
35836     },
35837
35838     render : function(bulkRender){
35839         var n = this.node, a = n.attributes;
35840         var targetNode = n.parentNode ?
35841               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35842
35843         if(!this.rendered){
35844             this.rendered = true;
35845
35846             this.renderElements(n, a, targetNode, bulkRender);
35847
35848             if(a.qtip){
35849                if(this.textNode.setAttributeNS){
35850                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35851                    if(a.qtipTitle){
35852                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35853                    }
35854                }else{
35855                    this.textNode.setAttribute("ext:qtip", a.qtip);
35856                    if(a.qtipTitle){
35857                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35858                    }
35859                }
35860             }else if(a.qtipCfg){
35861                 a.qtipCfg.target = Roo.id(this.textNode);
35862                 Roo.QuickTips.register(a.qtipCfg);
35863             }
35864             this.initEvents();
35865             if(!this.node.expanded){
35866                 this.updateExpandIcon();
35867             }
35868         }else{
35869             if(bulkRender === true) {
35870                 targetNode.appendChild(this.wrap);
35871             }
35872         }
35873     },
35874
35875     renderElements : function(n, a, targetNode, bulkRender)
35876     {
35877         // add some indent caching, this helps performance when rendering a large tree
35878         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35879         var t = n.getOwnerTree();
35880         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35881         if (typeof(n.attributes.html) != 'undefined') {
35882             txt = n.attributes.html;
35883         }
35884         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
35885         var cb = typeof a.checked == 'boolean';
35886         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35887         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35888             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35889             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35890             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35891             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35892             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35893              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35894                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35895             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35896             "</li>"];
35897
35898         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35899             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35900                                 n.nextSibling.ui.getEl(), buf.join(""));
35901         }else{
35902             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35903         }
35904
35905         this.elNode = this.wrap.childNodes[0];
35906         this.ctNode = this.wrap.childNodes[1];
35907         var cs = this.elNode.childNodes;
35908         this.indentNode = cs[0];
35909         this.ecNode = cs[1];
35910         this.iconNode = cs[2];
35911         var index = 3;
35912         if(cb){
35913             this.checkbox = cs[3];
35914             index++;
35915         }
35916         this.anchor = cs[index];
35917         this.textNode = cs[index].firstChild;
35918     },
35919
35920     getAnchor : function(){
35921         return this.anchor;
35922     },
35923
35924     getTextEl : function(){
35925         return this.textNode;
35926     },
35927
35928     getIconEl : function(){
35929         return this.iconNode;
35930     },
35931
35932     isChecked : function(){
35933         return this.checkbox ? this.checkbox.checked : false;
35934     },
35935
35936     updateExpandIcon : function(){
35937         if(this.rendered){
35938             var n = this.node, c1, c2;
35939             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35940             var hasChild = n.hasChildNodes();
35941             if(hasChild){
35942                 if(n.expanded){
35943                     cls += "-minus";
35944                     c1 = "x-tree-node-collapsed";
35945                     c2 = "x-tree-node-expanded";
35946                 }else{
35947                     cls += "-plus";
35948                     c1 = "x-tree-node-expanded";
35949                     c2 = "x-tree-node-collapsed";
35950                 }
35951                 if(this.wasLeaf){
35952                     this.removeClass("x-tree-node-leaf");
35953                     this.wasLeaf = false;
35954                 }
35955                 if(this.c1 != c1 || this.c2 != c2){
35956                     Roo.fly(this.elNode).replaceClass(c1, c2);
35957                     this.c1 = c1; this.c2 = c2;
35958                 }
35959             }else{
35960                 // this changes non-leafs into leafs if they have no children.
35961                 // it's not very rational behaviour..
35962                 
35963                 if(!this.wasLeaf && this.node.leaf){
35964                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35965                     delete this.c1;
35966                     delete this.c2;
35967                     this.wasLeaf = true;
35968                 }
35969             }
35970             var ecc = "x-tree-ec-icon "+cls;
35971             if(this.ecc != ecc){
35972                 this.ecNode.className = ecc;
35973                 this.ecc = ecc;
35974             }
35975         }
35976     },
35977
35978     getChildIndent : function(){
35979         if(!this.childIndent){
35980             var buf = [];
35981             var p = this.node;
35982             while(p){
35983                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35984                     if(!p.isLast()) {
35985                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35986                     } else {
35987                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35988                     }
35989                 }
35990                 p = p.parentNode;
35991             }
35992             this.childIndent = buf.join("");
35993         }
35994         return this.childIndent;
35995     },
35996
35997     renderIndent : function(){
35998         if(this.rendered){
35999             var indent = "";
36000             var p = this.node.parentNode;
36001             if(p){
36002                 indent = p.ui.getChildIndent();
36003             }
36004             if(this.indentMarkup != indent){ // don't rerender if not required
36005                 this.indentNode.innerHTML = indent;
36006                 this.indentMarkup = indent;
36007             }
36008             this.updateExpandIcon();
36009         }
36010     }
36011 };
36012
36013 Roo.tree.RootTreeNodeUI = function(){
36014     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36015 };
36016 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36017     render : function(){
36018         if(!this.rendered){
36019             var targetNode = this.node.ownerTree.innerCt.dom;
36020             this.node.expanded = true;
36021             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36022             this.wrap = this.ctNode = targetNode.firstChild;
36023         }
36024     },
36025     collapse : function(){
36026     },
36027     expand : function(){
36028     }
36029 });/*
36030  * Based on:
36031  * Ext JS Library 1.1.1
36032  * Copyright(c) 2006-2007, Ext JS, LLC.
36033  *
36034  * Originally Released Under LGPL - original licence link has changed is not relivant.
36035  *
36036  * Fork - LGPL
36037  * <script type="text/javascript">
36038  */
36039 /**
36040  * @class Roo.tree.TreeLoader
36041  * @extends Roo.util.Observable
36042  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36043  * nodes from a specified URL. The response must be a javascript Array definition
36044  * who's elements are node definition objects. eg:
36045  * <pre><code>
36046 {  success : true,
36047    data :      [
36048    
36049     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36050     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36051     ]
36052 }
36053
36054
36055 </code></pre>
36056  * <br><br>
36057  * The old style respose with just an array is still supported, but not recommended.
36058  * <br><br>
36059  *
36060  * A server request is sent, and child nodes are loaded only when a node is expanded.
36061  * The loading node's id is passed to the server under the parameter name "node" to
36062  * enable the server to produce the correct child nodes.
36063  * <br><br>
36064  * To pass extra parameters, an event handler may be attached to the "beforeload"
36065  * event, and the parameters specified in the TreeLoader's baseParams property:
36066  * <pre><code>
36067     myTreeLoader.on("beforeload", function(treeLoader, node) {
36068         this.baseParams.category = node.attributes.category;
36069     }, this);
36070     
36071 </code></pre>
36072  *
36073  * This would pass an HTTP parameter called "category" to the server containing
36074  * the value of the Node's "category" attribute.
36075  * @constructor
36076  * Creates a new Treeloader.
36077  * @param {Object} config A config object containing config properties.
36078  */
36079 Roo.tree.TreeLoader = function(config){
36080     this.baseParams = {};
36081     this.requestMethod = "POST";
36082     Roo.apply(this, config);
36083
36084     this.addEvents({
36085     
36086         /**
36087          * @event beforeload
36088          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36089          * @param {Object} This TreeLoader object.
36090          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36091          * @param {Object} callback The callback function specified in the {@link #load} call.
36092          */
36093         beforeload : true,
36094         /**
36095          * @event load
36096          * Fires when the node has been successfuly loaded.
36097          * @param {Object} This TreeLoader object.
36098          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36099          * @param {Object} response The response object containing the data from the server.
36100          */
36101         load : true,
36102         /**
36103          * @event loadexception
36104          * Fires if the network request failed.
36105          * @param {Object} This TreeLoader object.
36106          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36107          * @param {Object} response The response object containing the data from the server.
36108          */
36109         loadexception : true,
36110         /**
36111          * @event create
36112          * Fires before a node is created, enabling you to return custom Node types 
36113          * @param {Object} This TreeLoader object.
36114          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36115          */
36116         create : true
36117     });
36118
36119     Roo.tree.TreeLoader.superclass.constructor.call(this);
36120 };
36121
36122 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36123     /**
36124     * @cfg {String} dataUrl The URL from which to request a Json string which
36125     * specifies an array of node definition object representing the child nodes
36126     * to be loaded.
36127     */
36128     /**
36129     * @cfg {String} requestMethod either GET or POST
36130     * defaults to POST (due to BC)
36131     * to be loaded.
36132     */
36133     /**
36134     * @cfg {Object} baseParams (optional) An object containing properties which
36135     * specify HTTP parameters to be passed to each request for child nodes.
36136     */
36137     /**
36138     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36139     * created by this loader. If the attributes sent by the server have an attribute in this object,
36140     * they take priority.
36141     */
36142     /**
36143     * @cfg {Object} uiProviders (optional) An object containing properties which
36144     * 
36145     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36146     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36147     * <i>uiProvider</i> attribute of a returned child node is a string rather
36148     * than a reference to a TreeNodeUI implementation, this that string value
36149     * is used as a property name in the uiProviders object. You can define the provider named
36150     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36151     */
36152     uiProviders : {},
36153
36154     /**
36155     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36156     * child nodes before loading.
36157     */
36158     clearOnLoad : true,
36159
36160     /**
36161     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36162     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36163     * Grid query { data : [ .....] }
36164     */
36165     
36166     root : false,
36167      /**
36168     * @cfg {String} queryParam (optional) 
36169     * Name of the query as it will be passed on the querystring (defaults to 'node')
36170     * eg. the request will be ?node=[id]
36171     */
36172     
36173     
36174     queryParam: false,
36175     
36176     /**
36177      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36178      * This is called automatically when a node is expanded, but may be used to reload
36179      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36180      * @param {Roo.tree.TreeNode} node
36181      * @param {Function} callback
36182      */
36183     load : function(node, callback){
36184         if(this.clearOnLoad){
36185             while(node.firstChild){
36186                 node.removeChild(node.firstChild);
36187             }
36188         }
36189         if(node.attributes.children){ // preloaded json children
36190             var cs = node.attributes.children;
36191             for(var i = 0, len = cs.length; i < len; i++){
36192                 node.appendChild(this.createNode(cs[i]));
36193             }
36194             if(typeof callback == "function"){
36195                 callback();
36196             }
36197         }else if(this.dataUrl){
36198             this.requestData(node, callback);
36199         }
36200     },
36201
36202     getParams: function(node){
36203         var buf = [], bp = this.baseParams;
36204         for(var key in bp){
36205             if(typeof bp[key] != "function"){
36206                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36207             }
36208         }
36209         var n = this.queryParam === false ? 'node' : this.queryParam;
36210         buf.push(n + "=", encodeURIComponent(node.id));
36211         return buf.join("");
36212     },
36213
36214     requestData : function(node, callback){
36215         if(this.fireEvent("beforeload", this, node, callback) !== false){
36216             this.transId = Roo.Ajax.request({
36217                 method:this.requestMethod,
36218                 url: this.dataUrl||this.url,
36219                 success: this.handleResponse,
36220                 failure: this.handleFailure,
36221                 scope: this,
36222                 argument: {callback: callback, node: node},
36223                 params: this.getParams(node)
36224             });
36225         }else{
36226             // if the load is cancelled, make sure we notify
36227             // the node that we are done
36228             if(typeof callback == "function"){
36229                 callback();
36230             }
36231         }
36232     },
36233
36234     isLoading : function(){
36235         return this.transId ? true : false;
36236     },
36237
36238     abort : function(){
36239         if(this.isLoading()){
36240             Roo.Ajax.abort(this.transId);
36241         }
36242     },
36243
36244     // private
36245     createNode : function(attr)
36246     {
36247         // apply baseAttrs, nice idea Corey!
36248         if(this.baseAttrs){
36249             Roo.applyIf(attr, this.baseAttrs);
36250         }
36251         if(this.applyLoader !== false){
36252             attr.loader = this;
36253         }
36254         // uiProvider = depreciated..
36255         
36256         if(typeof(attr.uiProvider) == 'string'){
36257            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36258                 /**  eval:var:attr */ eval(attr.uiProvider);
36259         }
36260         if(typeof(this.uiProviders['default']) != 'undefined') {
36261             attr.uiProvider = this.uiProviders['default'];
36262         }
36263         
36264         this.fireEvent('create', this, attr);
36265         
36266         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36267         return(attr.leaf ?
36268                         new Roo.tree.TreeNode(attr) :
36269                         new Roo.tree.AsyncTreeNode(attr));
36270     },
36271
36272     processResponse : function(response, node, callback)
36273     {
36274         var json = response.responseText;
36275         try {
36276             
36277             var o = Roo.decode(json);
36278             
36279             if (this.root === false && typeof(o.success) != undefined) {
36280                 this.root = 'data'; // the default behaviour for list like data..
36281                 }
36282                 
36283             if (this.root !== false &&  !o.success) {
36284                 // it's a failure condition.
36285                 var a = response.argument;
36286                 this.fireEvent("loadexception", this, a.node, response);
36287                 Roo.log("Load failed - should have a handler really");
36288                 return;
36289             }
36290             
36291             
36292             
36293             if (this.root !== false) {
36294                  o = o[this.root];
36295             }
36296             
36297             for(var i = 0, len = o.length; i < len; i++){
36298                 var n = this.createNode(o[i]);
36299                 if(n){
36300                     node.appendChild(n);
36301                 }
36302             }
36303             if(typeof callback == "function"){
36304                 callback(this, node);
36305             }
36306         }catch(e){
36307             this.handleFailure(response);
36308         }
36309     },
36310
36311     handleResponse : function(response){
36312         this.transId = false;
36313         var a = response.argument;
36314         this.processResponse(response, a.node, a.callback);
36315         this.fireEvent("load", this, a.node, response);
36316     },
36317
36318     handleFailure : function(response)
36319     {
36320         // should handle failure better..
36321         this.transId = false;
36322         var a = response.argument;
36323         this.fireEvent("loadexception", this, a.node, response);
36324         if(typeof a.callback == "function"){
36325             a.callback(this, a.node);
36326         }
36327     }
36328 });/*
36329  * Based on:
36330  * Ext JS Library 1.1.1
36331  * Copyright(c) 2006-2007, Ext JS, LLC.
36332  *
36333  * Originally Released Under LGPL - original licence link has changed is not relivant.
36334  *
36335  * Fork - LGPL
36336  * <script type="text/javascript">
36337  */
36338
36339 /**
36340 * @class Roo.tree.TreeFilter
36341 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36342 * @param {TreePanel} tree
36343 * @param {Object} config (optional)
36344  */
36345 Roo.tree.TreeFilter = function(tree, config){
36346     this.tree = tree;
36347     this.filtered = {};
36348     Roo.apply(this, config);
36349 };
36350
36351 Roo.tree.TreeFilter.prototype = {
36352     clearBlank:false,
36353     reverse:false,
36354     autoClear:false,
36355     remove:false,
36356
36357      /**
36358      * Filter the data by a specific attribute.
36359      * @param {String/RegExp} value Either string that the attribute value
36360      * should start with or a RegExp to test against the attribute
36361      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36362      * @param {TreeNode} startNode (optional) The node to start the filter at.
36363      */
36364     filter : function(value, attr, startNode){
36365         attr = attr || "text";
36366         var f;
36367         if(typeof value == "string"){
36368             var vlen = value.length;
36369             // auto clear empty filter
36370             if(vlen == 0 && this.clearBlank){
36371                 this.clear();
36372                 return;
36373             }
36374             value = value.toLowerCase();
36375             f = function(n){
36376                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36377             };
36378         }else if(value.exec){ // regex?
36379             f = function(n){
36380                 return value.test(n.attributes[attr]);
36381             };
36382         }else{
36383             throw 'Illegal filter type, must be string or regex';
36384         }
36385         this.filterBy(f, null, startNode);
36386         },
36387
36388     /**
36389      * Filter by a function. The passed function will be called with each
36390      * node in the tree (or from the startNode). If the function returns true, the node is kept
36391      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36392      * @param {Function} fn The filter function
36393      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36394      */
36395     filterBy : function(fn, scope, startNode){
36396         startNode = startNode || this.tree.root;
36397         if(this.autoClear){
36398             this.clear();
36399         }
36400         var af = this.filtered, rv = this.reverse;
36401         var f = function(n){
36402             if(n == startNode){
36403                 return true;
36404             }
36405             if(af[n.id]){
36406                 return false;
36407             }
36408             var m = fn.call(scope || n, n);
36409             if(!m || rv){
36410                 af[n.id] = n;
36411                 n.ui.hide();
36412                 return false;
36413             }
36414             return true;
36415         };
36416         startNode.cascade(f);
36417         if(this.remove){
36418            for(var id in af){
36419                if(typeof id != "function"){
36420                    var n = af[id];
36421                    if(n && n.parentNode){
36422                        n.parentNode.removeChild(n);
36423                    }
36424                }
36425            }
36426         }
36427     },
36428
36429     /**
36430      * Clears the current filter. Note: with the "remove" option
36431      * set a filter cannot be cleared.
36432      */
36433     clear : function(){
36434         var t = this.tree;
36435         var af = this.filtered;
36436         for(var id in af){
36437             if(typeof id != "function"){
36438                 var n = af[id];
36439                 if(n){
36440                     n.ui.show();
36441                 }
36442             }
36443         }
36444         this.filtered = {};
36445     }
36446 };
36447 /*
36448  * Based on:
36449  * Ext JS Library 1.1.1
36450  * Copyright(c) 2006-2007, Ext JS, LLC.
36451  *
36452  * Originally Released Under LGPL - original licence link has changed is not relivant.
36453  *
36454  * Fork - LGPL
36455  * <script type="text/javascript">
36456  */
36457  
36458
36459 /**
36460  * @class Roo.tree.TreeSorter
36461  * Provides sorting of nodes in a TreePanel
36462  * 
36463  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36464  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36465  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36466  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36467  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36468  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36469  * @constructor
36470  * @param {TreePanel} tree
36471  * @param {Object} config
36472  */
36473 Roo.tree.TreeSorter = function(tree, config){
36474     Roo.apply(this, config);
36475     tree.on("beforechildrenrendered", this.doSort, this);
36476     tree.on("append", this.updateSort, this);
36477     tree.on("insert", this.updateSort, this);
36478     
36479     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36480     var p = this.property || "text";
36481     var sortType = this.sortType;
36482     var fs = this.folderSort;
36483     var cs = this.caseSensitive === true;
36484     var leafAttr = this.leafAttr || 'leaf';
36485
36486     this.sortFn = function(n1, n2){
36487         if(fs){
36488             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36489                 return 1;
36490             }
36491             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36492                 return -1;
36493             }
36494         }
36495         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36496         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36497         if(v1 < v2){
36498                         return dsc ? +1 : -1;
36499                 }else if(v1 > v2){
36500                         return dsc ? -1 : +1;
36501         }else{
36502                 return 0;
36503         }
36504     };
36505 };
36506
36507 Roo.tree.TreeSorter.prototype = {
36508     doSort : function(node){
36509         node.sort(this.sortFn);
36510     },
36511     
36512     compareNodes : function(n1, n2){
36513         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36514     },
36515     
36516     updateSort : function(tree, node){
36517         if(node.childrenRendered){
36518             this.doSort.defer(1, this, [node]);
36519         }
36520     }
36521 };/*
36522  * Based on:
36523  * Ext JS Library 1.1.1
36524  * Copyright(c) 2006-2007, Ext JS, LLC.
36525  *
36526  * Originally Released Under LGPL - original licence link has changed is not relivant.
36527  *
36528  * Fork - LGPL
36529  * <script type="text/javascript">
36530  */
36531
36532 if(Roo.dd.DropZone){
36533     
36534 Roo.tree.TreeDropZone = function(tree, config){
36535     this.allowParentInsert = false;
36536     this.allowContainerDrop = false;
36537     this.appendOnly = false;
36538     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36539     this.tree = tree;
36540     this.lastInsertClass = "x-tree-no-status";
36541     this.dragOverData = {};
36542 };
36543
36544 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36545     ddGroup : "TreeDD",
36546     scroll:  true,
36547     
36548     expandDelay : 1000,
36549     
36550     expandNode : function(node){
36551         if(node.hasChildNodes() && !node.isExpanded()){
36552             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36553         }
36554     },
36555     
36556     queueExpand : function(node){
36557         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36558     },
36559     
36560     cancelExpand : function(){
36561         if(this.expandProcId){
36562             clearTimeout(this.expandProcId);
36563             this.expandProcId = false;
36564         }
36565     },
36566     
36567     isValidDropPoint : function(n, pt, dd, e, data){
36568         if(!n || !data){ return false; }
36569         var targetNode = n.node;
36570         var dropNode = data.node;
36571         // default drop rules
36572         if(!(targetNode && targetNode.isTarget && pt)){
36573             return false;
36574         }
36575         if(pt == "append" && targetNode.allowChildren === false){
36576             return false;
36577         }
36578         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36579             return false;
36580         }
36581         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36582             return false;
36583         }
36584         // reuse the object
36585         var overEvent = this.dragOverData;
36586         overEvent.tree = this.tree;
36587         overEvent.target = targetNode;
36588         overEvent.data = data;
36589         overEvent.point = pt;
36590         overEvent.source = dd;
36591         overEvent.rawEvent = e;
36592         overEvent.dropNode = dropNode;
36593         overEvent.cancel = false;  
36594         var result = this.tree.fireEvent("nodedragover", overEvent);
36595         return overEvent.cancel === false && result !== false;
36596     },
36597     
36598     getDropPoint : function(e, n, dd)
36599     {
36600         var tn = n.node;
36601         if(tn.isRoot){
36602             return tn.allowChildren !== false ? "append" : false; // always append for root
36603         }
36604         var dragEl = n.ddel;
36605         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36606         var y = Roo.lib.Event.getPageY(e);
36607         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36608         
36609         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36610         var noAppend = tn.allowChildren === false;
36611         if(this.appendOnly || tn.parentNode.allowChildren === false){
36612             return noAppend ? false : "append";
36613         }
36614         var noBelow = false;
36615         if(!this.allowParentInsert){
36616             noBelow = tn.hasChildNodes() && tn.isExpanded();
36617         }
36618         var q = (b - t) / (noAppend ? 2 : 3);
36619         if(y >= t && y < (t + q)){
36620             return "above";
36621         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36622             return "below";
36623         }else{
36624             return "append";
36625         }
36626     },
36627     
36628     onNodeEnter : function(n, dd, e, data)
36629     {
36630         this.cancelExpand();
36631     },
36632     
36633     onNodeOver : function(n, dd, e, data)
36634     {
36635        
36636         var pt = this.getDropPoint(e, n, dd);
36637         var node = n.node;
36638         
36639         // auto node expand check
36640         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36641             this.queueExpand(node);
36642         }else if(pt != "append"){
36643             this.cancelExpand();
36644         }
36645         
36646         // set the insert point style on the target node
36647         var returnCls = this.dropNotAllowed;
36648         if(this.isValidDropPoint(n, pt, dd, e, data)){
36649            if(pt){
36650                var el = n.ddel;
36651                var cls;
36652                if(pt == "above"){
36653                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36654                    cls = "x-tree-drag-insert-above";
36655                }else if(pt == "below"){
36656                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36657                    cls = "x-tree-drag-insert-below";
36658                }else{
36659                    returnCls = "x-tree-drop-ok-append";
36660                    cls = "x-tree-drag-append";
36661                }
36662                if(this.lastInsertClass != cls){
36663                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36664                    this.lastInsertClass = cls;
36665                }
36666            }
36667        }
36668        return returnCls;
36669     },
36670     
36671     onNodeOut : function(n, dd, e, data){
36672         
36673         this.cancelExpand();
36674         this.removeDropIndicators(n);
36675     },
36676     
36677     onNodeDrop : function(n, dd, e, data){
36678         var point = this.getDropPoint(e, n, dd);
36679         var targetNode = n.node;
36680         targetNode.ui.startDrop();
36681         if(!this.isValidDropPoint(n, point, dd, e, data)){
36682             targetNode.ui.endDrop();
36683             return false;
36684         }
36685         // first try to find the drop node
36686         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36687         var dropEvent = {
36688             tree : this.tree,
36689             target: targetNode,
36690             data: data,
36691             point: point,
36692             source: dd,
36693             rawEvent: e,
36694             dropNode: dropNode,
36695             cancel: !dropNode   
36696         };
36697         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36698         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36699             targetNode.ui.endDrop();
36700             return false;
36701         }
36702         // allow target changing
36703         targetNode = dropEvent.target;
36704         if(point == "append" && !targetNode.isExpanded()){
36705             targetNode.expand(false, null, function(){
36706                 this.completeDrop(dropEvent);
36707             }.createDelegate(this));
36708         }else{
36709             this.completeDrop(dropEvent);
36710         }
36711         return true;
36712     },
36713     
36714     completeDrop : function(de){
36715         var ns = de.dropNode, p = de.point, t = de.target;
36716         if(!(ns instanceof Array)){
36717             ns = [ns];
36718         }
36719         var n;
36720         for(var i = 0, len = ns.length; i < len; i++){
36721             n = ns[i];
36722             if(p == "above"){
36723                 t.parentNode.insertBefore(n, t);
36724             }else if(p == "below"){
36725                 t.parentNode.insertBefore(n, t.nextSibling);
36726             }else{
36727                 t.appendChild(n);
36728             }
36729         }
36730         n.ui.focus();
36731         if(this.tree.hlDrop){
36732             n.ui.highlight();
36733         }
36734         t.ui.endDrop();
36735         this.tree.fireEvent("nodedrop", de);
36736     },
36737     
36738     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36739         if(this.tree.hlDrop){
36740             dropNode.ui.focus();
36741             dropNode.ui.highlight();
36742         }
36743         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36744     },
36745     
36746     getTree : function(){
36747         return this.tree;
36748     },
36749     
36750     removeDropIndicators : function(n){
36751         if(n && n.ddel){
36752             var el = n.ddel;
36753             Roo.fly(el).removeClass([
36754                     "x-tree-drag-insert-above",
36755                     "x-tree-drag-insert-below",
36756                     "x-tree-drag-append"]);
36757             this.lastInsertClass = "_noclass";
36758         }
36759     },
36760     
36761     beforeDragDrop : function(target, e, id){
36762         this.cancelExpand();
36763         return true;
36764     },
36765     
36766     afterRepair : function(data){
36767         if(data && Roo.enableFx){
36768             data.node.ui.highlight();
36769         }
36770         this.hideProxy();
36771     } 
36772     
36773 });
36774
36775 }
36776 /*
36777  * Based on:
36778  * Ext JS Library 1.1.1
36779  * Copyright(c) 2006-2007, Ext JS, LLC.
36780  *
36781  * Originally Released Under LGPL - original licence link has changed is not relivant.
36782  *
36783  * Fork - LGPL
36784  * <script type="text/javascript">
36785  */
36786  
36787
36788 if(Roo.dd.DragZone){
36789 Roo.tree.TreeDragZone = function(tree, config){
36790     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36791     this.tree = tree;
36792 };
36793
36794 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36795     ddGroup : "TreeDD",
36796    
36797     onBeforeDrag : function(data, e){
36798         var n = data.node;
36799         return n && n.draggable && !n.disabled;
36800     },
36801      
36802     
36803     onInitDrag : function(e){
36804         var data = this.dragData;
36805         this.tree.getSelectionModel().select(data.node);
36806         this.proxy.update("");
36807         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36808         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36809     },
36810     
36811     getRepairXY : function(e, data){
36812         return data.node.ui.getDDRepairXY();
36813     },
36814     
36815     onEndDrag : function(data, e){
36816         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36817         
36818         
36819     },
36820     
36821     onValidDrop : function(dd, e, id){
36822         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36823         this.hideProxy();
36824     },
36825     
36826     beforeInvalidDrop : function(e, id){
36827         // this scrolls the original position back into view
36828         var sm = this.tree.getSelectionModel();
36829         sm.clearSelections();
36830         sm.select(this.dragData.node);
36831     }
36832 });
36833 }/*
36834  * Based on:
36835  * Ext JS Library 1.1.1
36836  * Copyright(c) 2006-2007, Ext JS, LLC.
36837  *
36838  * Originally Released Under LGPL - original licence link has changed is not relivant.
36839  *
36840  * Fork - LGPL
36841  * <script type="text/javascript">
36842  */
36843 /**
36844  * @class Roo.tree.TreeEditor
36845  * @extends Roo.Editor
36846  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36847  * as the editor field.
36848  * @constructor
36849  * @param {Object} config (used to be the tree panel.)
36850  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36851  * 
36852  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36853  * @cfg {Roo.form.TextField|Object} field The field configuration
36854  *
36855  * 
36856  */
36857 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36858     var tree = config;
36859     var field;
36860     if (oldconfig) { // old style..
36861         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36862     } else {
36863         // new style..
36864         tree = config.tree;
36865         config.field = config.field  || {};
36866         config.field.xtype = 'TextField';
36867         field = Roo.factory(config.field, Roo.form);
36868     }
36869     config = config || {};
36870     
36871     
36872     this.addEvents({
36873         /**
36874          * @event beforenodeedit
36875          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36876          * false from the handler of this event.
36877          * @param {Editor} this
36878          * @param {Roo.tree.Node} node 
36879          */
36880         "beforenodeedit" : true
36881     });
36882     
36883     //Roo.log(config);
36884     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36885
36886     this.tree = tree;
36887
36888     tree.on('beforeclick', this.beforeNodeClick, this);
36889     tree.getTreeEl().on('mousedown', this.hide, this);
36890     this.on('complete', this.updateNode, this);
36891     this.on('beforestartedit', this.fitToTree, this);
36892     this.on('startedit', this.bindScroll, this, {delay:10});
36893     this.on('specialkey', this.onSpecialKey, this);
36894 };
36895
36896 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36897     /**
36898      * @cfg {String} alignment
36899      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36900      */
36901     alignment: "l-l",
36902     // inherit
36903     autoSize: false,
36904     /**
36905      * @cfg {Boolean} hideEl
36906      * True to hide the bound element while the editor is displayed (defaults to false)
36907      */
36908     hideEl : false,
36909     /**
36910      * @cfg {String} cls
36911      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36912      */
36913     cls: "x-small-editor x-tree-editor",
36914     /**
36915      * @cfg {Boolean} shim
36916      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36917      */
36918     shim:false,
36919     // inherit
36920     shadow:"frame",
36921     /**
36922      * @cfg {Number} maxWidth
36923      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36924      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36925      * scroll and client offsets into account prior to each edit.
36926      */
36927     maxWidth: 250,
36928
36929     editDelay : 350,
36930
36931     // private
36932     fitToTree : function(ed, el){
36933         var td = this.tree.getTreeEl().dom, nd = el.dom;
36934         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36935             td.scrollLeft = nd.offsetLeft;
36936         }
36937         var w = Math.min(
36938                 this.maxWidth,
36939                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36940         this.setSize(w, '');
36941         
36942         return this.fireEvent('beforenodeedit', this, this.editNode);
36943         
36944     },
36945
36946     // private
36947     triggerEdit : function(node){
36948         this.completeEdit();
36949         this.editNode = node;
36950         this.startEdit(node.ui.textNode, node.text);
36951     },
36952
36953     // private
36954     bindScroll : function(){
36955         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36956     },
36957
36958     // private
36959     beforeNodeClick : function(node, e){
36960         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36961         this.lastClick = new Date();
36962         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36963             e.stopEvent();
36964             this.triggerEdit(node);
36965             return false;
36966         }
36967         return true;
36968     },
36969
36970     // private
36971     updateNode : function(ed, value){
36972         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36973         this.editNode.setText(value);
36974     },
36975
36976     // private
36977     onHide : function(){
36978         Roo.tree.TreeEditor.superclass.onHide.call(this);
36979         if(this.editNode){
36980             this.editNode.ui.focus();
36981         }
36982     },
36983
36984     // private
36985     onSpecialKey : function(field, e){
36986         var k = e.getKey();
36987         if(k == e.ESC){
36988             e.stopEvent();
36989             this.cancelEdit();
36990         }else if(k == e.ENTER && !e.hasModifier()){
36991             e.stopEvent();
36992             this.completeEdit();
36993         }
36994     }
36995 });//<Script type="text/javascript">
36996 /*
36997  * Based on:
36998  * Ext JS Library 1.1.1
36999  * Copyright(c) 2006-2007, Ext JS, LLC.
37000  *
37001  * Originally Released Under LGPL - original licence link has changed is not relivant.
37002  *
37003  * Fork - LGPL
37004  * <script type="text/javascript">
37005  */
37006  
37007 /**
37008  * Not documented??? - probably should be...
37009  */
37010
37011 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37012     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37013     
37014     renderElements : function(n, a, targetNode, bulkRender){
37015         //consel.log("renderElements?");
37016         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37017
37018         var t = n.getOwnerTree();
37019         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37020         
37021         var cols = t.columns;
37022         var bw = t.borderWidth;
37023         var c = cols[0];
37024         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37025          var cb = typeof a.checked == "boolean";
37026         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37027         var colcls = 'x-t-' + tid + '-c0';
37028         var buf = [
37029             '<li class="x-tree-node">',
37030             
37031                 
37032                 '<div class="x-tree-node-el ', a.cls,'">',
37033                     // extran...
37034                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37035                 
37036                 
37037                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37038                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37039                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37040                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37041                            (a.iconCls ? ' '+a.iconCls : ''),
37042                            '" unselectable="on" />',
37043                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37044                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37045                              
37046                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37047                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37048                             '<span unselectable="on" qtip="' + tx + '">',
37049                              tx,
37050                              '</span></a>' ,
37051                     '</div>',
37052                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37053                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37054                  ];
37055         for(var i = 1, len = cols.length; i < len; i++){
37056             c = cols[i];
37057             colcls = 'x-t-' + tid + '-c' +i;
37058             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37059             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37060                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37061                       "</div>");
37062          }
37063          
37064          buf.push(
37065             '</a>',
37066             '<div class="x-clear"></div></div>',
37067             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37068             "</li>");
37069         
37070         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37071             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37072                                 n.nextSibling.ui.getEl(), buf.join(""));
37073         }else{
37074             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37075         }
37076         var el = this.wrap.firstChild;
37077         this.elRow = el;
37078         this.elNode = el.firstChild;
37079         this.ranchor = el.childNodes[1];
37080         this.ctNode = this.wrap.childNodes[1];
37081         var cs = el.firstChild.childNodes;
37082         this.indentNode = cs[0];
37083         this.ecNode = cs[1];
37084         this.iconNode = cs[2];
37085         var index = 3;
37086         if(cb){
37087             this.checkbox = cs[3];
37088             index++;
37089         }
37090         this.anchor = cs[index];
37091         
37092         this.textNode = cs[index].firstChild;
37093         
37094         //el.on("click", this.onClick, this);
37095         //el.on("dblclick", this.onDblClick, this);
37096         
37097         
37098        // console.log(this);
37099     },
37100     initEvents : function(){
37101         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37102         
37103             
37104         var a = this.ranchor;
37105
37106         var el = Roo.get(a);
37107
37108         if(Roo.isOpera){ // opera render bug ignores the CSS
37109             el.setStyle("text-decoration", "none");
37110         }
37111
37112         el.on("click", this.onClick, this);
37113         el.on("dblclick", this.onDblClick, this);
37114         el.on("contextmenu", this.onContextMenu, this);
37115         
37116     },
37117     
37118     /*onSelectedChange : function(state){
37119         if(state){
37120             this.focus();
37121             this.addClass("x-tree-selected");
37122         }else{
37123             //this.blur();
37124             this.removeClass("x-tree-selected");
37125         }
37126     },*/
37127     addClass : function(cls){
37128         if(this.elRow){
37129             Roo.fly(this.elRow).addClass(cls);
37130         }
37131         
37132     },
37133     
37134     
37135     removeClass : function(cls){
37136         if(this.elRow){
37137             Roo.fly(this.elRow).removeClass(cls);
37138         }
37139     }
37140
37141     
37142     
37143 });//<Script type="text/javascript">
37144
37145 /*
37146  * Based on:
37147  * Ext JS Library 1.1.1
37148  * Copyright(c) 2006-2007, Ext JS, LLC.
37149  *
37150  * Originally Released Under LGPL - original licence link has changed is not relivant.
37151  *
37152  * Fork - LGPL
37153  * <script type="text/javascript">
37154  */
37155  
37156
37157 /**
37158  * @class Roo.tree.ColumnTree
37159  * @extends Roo.data.TreePanel
37160  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37161  * @cfg {int} borderWidth  compined right/left border allowance
37162  * @constructor
37163  * @param {String/HTMLElement/Element} el The container element
37164  * @param {Object} config
37165  */
37166 Roo.tree.ColumnTree =  function(el, config)
37167 {
37168    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37169    this.addEvents({
37170         /**
37171         * @event resize
37172         * Fire this event on a container when it resizes
37173         * @param {int} w Width
37174         * @param {int} h Height
37175         */
37176        "resize" : true
37177     });
37178     this.on('resize', this.onResize, this);
37179 };
37180
37181 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37182     //lines:false,
37183     
37184     
37185     borderWidth: Roo.isBorderBox ? 0 : 2, 
37186     headEls : false,
37187     
37188     render : function(){
37189         // add the header.....
37190        
37191         Roo.tree.ColumnTree.superclass.render.apply(this);
37192         
37193         this.el.addClass('x-column-tree');
37194         
37195         this.headers = this.el.createChild(
37196             {cls:'x-tree-headers'},this.innerCt.dom);
37197    
37198         var cols = this.columns, c;
37199         var totalWidth = 0;
37200         this.headEls = [];
37201         var  len = cols.length;
37202         for(var i = 0; i < len; i++){
37203              c = cols[i];
37204              totalWidth += c.width;
37205             this.headEls.push(this.headers.createChild({
37206                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37207                  cn: {
37208                      cls:'x-tree-hd-text',
37209                      html: c.header
37210                  },
37211                  style:'width:'+(c.width-this.borderWidth)+'px;'
37212              }));
37213         }
37214         this.headers.createChild({cls:'x-clear'});
37215         // prevent floats from wrapping when clipped
37216         this.headers.setWidth(totalWidth);
37217         //this.innerCt.setWidth(totalWidth);
37218         this.innerCt.setStyle({ overflow: 'auto' });
37219         this.onResize(this.width, this.height);
37220              
37221         
37222     },
37223     onResize : function(w,h)
37224     {
37225         this.height = h;
37226         this.width = w;
37227         // resize cols..
37228         this.innerCt.setWidth(this.width);
37229         this.innerCt.setHeight(this.height-20);
37230         
37231         // headers...
37232         var cols = this.columns, c;
37233         var totalWidth = 0;
37234         var expEl = false;
37235         var len = cols.length;
37236         for(var i = 0; i < len; i++){
37237             c = cols[i];
37238             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37239                 // it's the expander..
37240                 expEl  = this.headEls[i];
37241                 continue;
37242             }
37243             totalWidth += c.width;
37244             
37245         }
37246         if (expEl) {
37247             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37248         }
37249         this.headers.setWidth(w-20);
37250
37251         
37252         
37253         
37254     }
37255 });
37256 /*
37257  * Based on:
37258  * Ext JS Library 1.1.1
37259  * Copyright(c) 2006-2007, Ext JS, LLC.
37260  *
37261  * Originally Released Under LGPL - original licence link has changed is not relivant.
37262  *
37263  * Fork - LGPL
37264  * <script type="text/javascript">
37265  */
37266  
37267 /**
37268  * @class Roo.menu.Menu
37269  * @extends Roo.util.Observable
37270  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37271  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37272  * @constructor
37273  * Creates a new Menu
37274  * @param {Object} config Configuration options
37275  */
37276 Roo.menu.Menu = function(config){
37277     
37278     Roo.menu.Menu.superclass.constructor.call(this, config);
37279     
37280     this.id = this.id || Roo.id();
37281     this.addEvents({
37282         /**
37283          * @event beforeshow
37284          * Fires before this menu is displayed
37285          * @param {Roo.menu.Menu} this
37286          */
37287         beforeshow : true,
37288         /**
37289          * @event beforehide
37290          * Fires before this menu is hidden
37291          * @param {Roo.menu.Menu} this
37292          */
37293         beforehide : true,
37294         /**
37295          * @event show
37296          * Fires after this menu is displayed
37297          * @param {Roo.menu.Menu} this
37298          */
37299         show : true,
37300         /**
37301          * @event hide
37302          * Fires after this menu is hidden
37303          * @param {Roo.menu.Menu} this
37304          */
37305         hide : true,
37306         /**
37307          * @event click
37308          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37309          * @param {Roo.menu.Menu} this
37310          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37311          * @param {Roo.EventObject} e
37312          */
37313         click : true,
37314         /**
37315          * @event mouseover
37316          * Fires when the mouse is hovering over this menu
37317          * @param {Roo.menu.Menu} this
37318          * @param {Roo.EventObject} e
37319          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37320          */
37321         mouseover : true,
37322         /**
37323          * @event mouseout
37324          * Fires when the mouse exits this menu
37325          * @param {Roo.menu.Menu} this
37326          * @param {Roo.EventObject} e
37327          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37328          */
37329         mouseout : true,
37330         /**
37331          * @event itemclick
37332          * Fires when a menu item contained in this menu is clicked
37333          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37334          * @param {Roo.EventObject} e
37335          */
37336         itemclick: true
37337     });
37338     if (this.registerMenu) {
37339         Roo.menu.MenuMgr.register(this);
37340     }
37341     
37342     var mis = this.items;
37343     this.items = new Roo.util.MixedCollection();
37344     if(mis){
37345         this.add.apply(this, mis);
37346     }
37347 };
37348
37349 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37350     /**
37351      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37352      */
37353     minWidth : 120,
37354     /**
37355      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37356      * for bottom-right shadow (defaults to "sides")
37357      */
37358     shadow : "sides",
37359     /**
37360      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37361      * this menu (defaults to "tl-tr?")
37362      */
37363     subMenuAlign : "tl-tr?",
37364     /**
37365      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37366      * relative to its element of origin (defaults to "tl-bl?")
37367      */
37368     defaultAlign : "tl-bl?",
37369     /**
37370      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37371      */
37372     allowOtherMenus : false,
37373     /**
37374      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37375      */
37376     registerMenu : true,
37377
37378     hidden:true,
37379
37380     // private
37381     render : function(){
37382         if(this.el){
37383             return;
37384         }
37385         var el = this.el = new Roo.Layer({
37386             cls: "x-menu",
37387             shadow:this.shadow,
37388             constrain: false,
37389             parentEl: this.parentEl || document.body,
37390             zindex:15000
37391         });
37392
37393         this.keyNav = new Roo.menu.MenuNav(this);
37394
37395         if(this.plain){
37396             el.addClass("x-menu-plain");
37397         }
37398         if(this.cls){
37399             el.addClass(this.cls);
37400         }
37401         // generic focus element
37402         this.focusEl = el.createChild({
37403             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37404         });
37405         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37406         //disabling touch- as it's causing issues ..
37407         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37408         ul.on('click'   , this.onClick, this);
37409         
37410         
37411         ul.on("mouseover", this.onMouseOver, this);
37412         ul.on("mouseout", this.onMouseOut, this);
37413         this.items.each(function(item){
37414             if (item.hidden) {
37415                 return;
37416             }
37417             
37418             var li = document.createElement("li");
37419             li.className = "x-menu-list-item";
37420             ul.dom.appendChild(li);
37421             item.render(li, this);
37422         }, this);
37423         this.ul = ul;
37424         this.autoWidth();
37425     },
37426
37427     // private
37428     autoWidth : function(){
37429         var el = this.el, ul = this.ul;
37430         if(!el){
37431             return;
37432         }
37433         var w = this.width;
37434         if(w){
37435             el.setWidth(w);
37436         }else if(Roo.isIE){
37437             el.setWidth(this.minWidth);
37438             var t = el.dom.offsetWidth; // force recalc
37439             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37440         }
37441     },
37442
37443     // private
37444     delayAutoWidth : function(){
37445         if(this.rendered){
37446             if(!this.awTask){
37447                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37448             }
37449             this.awTask.delay(20);
37450         }
37451     },
37452
37453     // private
37454     findTargetItem : function(e){
37455         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37456         if(t && t.menuItemId){
37457             return this.items.get(t.menuItemId);
37458         }
37459     },
37460
37461     // private
37462     onClick : function(e){
37463         Roo.log("menu.onClick");
37464         var t = this.findTargetItem(e);
37465         if(!t){
37466             return;
37467         }
37468         Roo.log(e);
37469         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37470             if(t == this.activeItem && t.shouldDeactivate(e)){
37471                 this.activeItem.deactivate();
37472                 delete this.activeItem;
37473                 return;
37474             }
37475             if(t.canActivate){
37476                 this.setActiveItem(t, true);
37477             }
37478             return;
37479             
37480             
37481         }
37482         
37483         t.onClick(e);
37484         this.fireEvent("click", this, t, e);
37485     },
37486
37487     // private
37488     setActiveItem : function(item, autoExpand){
37489         if(item != this.activeItem){
37490             if(this.activeItem){
37491                 this.activeItem.deactivate();
37492             }
37493             this.activeItem = item;
37494             item.activate(autoExpand);
37495         }else if(autoExpand){
37496             item.expandMenu();
37497         }
37498     },
37499
37500     // private
37501     tryActivate : function(start, step){
37502         var items = this.items;
37503         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37504             var item = items.get(i);
37505             if(!item.disabled && item.canActivate){
37506                 this.setActiveItem(item, false);
37507                 return item;
37508             }
37509         }
37510         return false;
37511     },
37512
37513     // private
37514     onMouseOver : function(e){
37515         var t;
37516         if(t = this.findTargetItem(e)){
37517             if(t.canActivate && !t.disabled){
37518                 this.setActiveItem(t, true);
37519             }
37520         }
37521         this.fireEvent("mouseover", this, e, t);
37522     },
37523
37524     // private
37525     onMouseOut : function(e){
37526         var t;
37527         if(t = this.findTargetItem(e)){
37528             if(t == this.activeItem && t.shouldDeactivate(e)){
37529                 this.activeItem.deactivate();
37530                 delete this.activeItem;
37531             }
37532         }
37533         this.fireEvent("mouseout", this, e, t);
37534     },
37535
37536     /**
37537      * Read-only.  Returns true if the menu is currently displayed, else false.
37538      * @type Boolean
37539      */
37540     isVisible : function(){
37541         return this.el && !this.hidden;
37542     },
37543
37544     /**
37545      * Displays this menu relative to another element
37546      * @param {String/HTMLElement/Roo.Element} element The element to align to
37547      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37548      * the element (defaults to this.defaultAlign)
37549      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37550      */
37551     show : function(el, pos, parentMenu){
37552         this.parentMenu = parentMenu;
37553         if(!this.el){
37554             this.render();
37555         }
37556         this.fireEvent("beforeshow", this);
37557         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37558     },
37559
37560     /**
37561      * Displays this menu at a specific xy position
37562      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37563      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37564      */
37565     showAt : function(xy, parentMenu, /* private: */_e){
37566         this.parentMenu = parentMenu;
37567         if(!this.el){
37568             this.render();
37569         }
37570         if(_e !== false){
37571             this.fireEvent("beforeshow", this);
37572             xy = this.el.adjustForConstraints(xy);
37573         }
37574         this.el.setXY(xy);
37575         this.el.show();
37576         this.hidden = false;
37577         this.focus();
37578         this.fireEvent("show", this);
37579     },
37580
37581     focus : function(){
37582         if(!this.hidden){
37583             this.doFocus.defer(50, this);
37584         }
37585     },
37586
37587     doFocus : function(){
37588         if(!this.hidden){
37589             this.focusEl.focus();
37590         }
37591     },
37592
37593     /**
37594      * Hides this menu and optionally all parent menus
37595      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37596      */
37597     hide : function(deep){
37598         if(this.el && this.isVisible()){
37599             this.fireEvent("beforehide", this);
37600             if(this.activeItem){
37601                 this.activeItem.deactivate();
37602                 this.activeItem = null;
37603             }
37604             this.el.hide();
37605             this.hidden = true;
37606             this.fireEvent("hide", this);
37607         }
37608         if(deep === true && this.parentMenu){
37609             this.parentMenu.hide(true);
37610         }
37611     },
37612
37613     /**
37614      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37615      * Any of the following are valid:
37616      * <ul>
37617      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37618      * <li>An HTMLElement object which will be converted to a menu item</li>
37619      * <li>A menu item config object that will be created as a new menu item</li>
37620      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37621      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37622      * </ul>
37623      * Usage:
37624      * <pre><code>
37625 // Create the menu
37626 var menu = new Roo.menu.Menu();
37627
37628 // Create a menu item to add by reference
37629 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37630
37631 // Add a bunch of items at once using different methods.
37632 // Only the last item added will be returned.
37633 var item = menu.add(
37634     menuItem,                // add existing item by ref
37635     'Dynamic Item',          // new TextItem
37636     '-',                     // new separator
37637     { text: 'Config Item' }  // new item by config
37638 );
37639 </code></pre>
37640      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37641      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37642      */
37643     add : function(){
37644         var a = arguments, l = a.length, item;
37645         for(var i = 0; i < l; i++){
37646             var el = a[i];
37647             if ((typeof(el) == "object") && el.xtype && el.xns) {
37648                 el = Roo.factory(el, Roo.menu);
37649             }
37650             
37651             if(el.render){ // some kind of Item
37652                 item = this.addItem(el);
37653             }else if(typeof el == "string"){ // string
37654                 if(el == "separator" || el == "-"){
37655                     item = this.addSeparator();
37656                 }else{
37657                     item = this.addText(el);
37658                 }
37659             }else if(el.tagName || el.el){ // element
37660                 item = this.addElement(el);
37661             }else if(typeof el == "object"){ // must be menu item config?
37662                 item = this.addMenuItem(el);
37663             }
37664         }
37665         return item;
37666     },
37667
37668     /**
37669      * Returns this menu's underlying {@link Roo.Element} object
37670      * @return {Roo.Element} The element
37671      */
37672     getEl : function(){
37673         if(!this.el){
37674             this.render();
37675         }
37676         return this.el;
37677     },
37678
37679     /**
37680      * Adds a separator bar to the menu
37681      * @return {Roo.menu.Item} The menu item that was added
37682      */
37683     addSeparator : function(){
37684         return this.addItem(new Roo.menu.Separator());
37685     },
37686
37687     /**
37688      * Adds an {@link Roo.Element} object to the menu
37689      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37690      * @return {Roo.menu.Item} The menu item that was added
37691      */
37692     addElement : function(el){
37693         return this.addItem(new Roo.menu.BaseItem(el));
37694     },
37695
37696     /**
37697      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37698      * @param {Roo.menu.Item} item The menu item to add
37699      * @return {Roo.menu.Item} The menu item that was added
37700      */
37701     addItem : function(item){
37702         this.items.add(item);
37703         if(this.ul){
37704             var li = document.createElement("li");
37705             li.className = "x-menu-list-item";
37706             this.ul.dom.appendChild(li);
37707             item.render(li, this);
37708             this.delayAutoWidth();
37709         }
37710         return item;
37711     },
37712
37713     /**
37714      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37715      * @param {Object} config A MenuItem config object
37716      * @return {Roo.menu.Item} The menu item that was added
37717      */
37718     addMenuItem : function(config){
37719         if(!(config instanceof Roo.menu.Item)){
37720             if(typeof config.checked == "boolean"){ // must be check menu item config?
37721                 config = new Roo.menu.CheckItem(config);
37722             }else{
37723                 config = new Roo.menu.Item(config);
37724             }
37725         }
37726         return this.addItem(config);
37727     },
37728
37729     /**
37730      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37731      * @param {String} text The text to display in the menu item
37732      * @return {Roo.menu.Item} The menu item that was added
37733      */
37734     addText : function(text){
37735         return this.addItem(new Roo.menu.TextItem({ text : text }));
37736     },
37737
37738     /**
37739      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37740      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37741      * @param {Roo.menu.Item} item The menu item to add
37742      * @return {Roo.menu.Item} The menu item that was added
37743      */
37744     insert : function(index, item){
37745         this.items.insert(index, item);
37746         if(this.ul){
37747             var li = document.createElement("li");
37748             li.className = "x-menu-list-item";
37749             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37750             item.render(li, this);
37751             this.delayAutoWidth();
37752         }
37753         return item;
37754     },
37755
37756     /**
37757      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37758      * @param {Roo.menu.Item} item The menu item to remove
37759      */
37760     remove : function(item){
37761         this.items.removeKey(item.id);
37762         item.destroy();
37763     },
37764
37765     /**
37766      * Removes and destroys all items in the menu
37767      */
37768     removeAll : function(){
37769         var f;
37770         while(f = this.items.first()){
37771             this.remove(f);
37772         }
37773     }
37774 });
37775
37776 // MenuNav is a private utility class used internally by the Menu
37777 Roo.menu.MenuNav = function(menu){
37778     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37779     this.scope = this.menu = menu;
37780 };
37781
37782 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37783     doRelay : function(e, h){
37784         var k = e.getKey();
37785         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37786             this.menu.tryActivate(0, 1);
37787             return false;
37788         }
37789         return h.call(this.scope || this, e, this.menu);
37790     },
37791
37792     up : function(e, m){
37793         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37794             m.tryActivate(m.items.length-1, -1);
37795         }
37796     },
37797
37798     down : function(e, m){
37799         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37800             m.tryActivate(0, 1);
37801         }
37802     },
37803
37804     right : function(e, m){
37805         if(m.activeItem){
37806             m.activeItem.expandMenu(true);
37807         }
37808     },
37809
37810     left : function(e, m){
37811         m.hide();
37812         if(m.parentMenu && m.parentMenu.activeItem){
37813             m.parentMenu.activeItem.activate();
37814         }
37815     },
37816
37817     enter : function(e, m){
37818         if(m.activeItem){
37819             e.stopPropagation();
37820             m.activeItem.onClick(e);
37821             m.fireEvent("click", this, m.activeItem);
37822             return true;
37823         }
37824     }
37825 });/*
37826  * Based on:
37827  * Ext JS Library 1.1.1
37828  * Copyright(c) 2006-2007, Ext JS, LLC.
37829  *
37830  * Originally Released Under LGPL - original licence link has changed is not relivant.
37831  *
37832  * Fork - LGPL
37833  * <script type="text/javascript">
37834  */
37835  
37836 /**
37837  * @class Roo.menu.MenuMgr
37838  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37839  * @singleton
37840  */
37841 Roo.menu.MenuMgr = function(){
37842    var menus, active, groups = {}, attached = false, lastShow = new Date();
37843
37844    // private - called when first menu is created
37845    function init(){
37846        menus = {};
37847        active = new Roo.util.MixedCollection();
37848        Roo.get(document).addKeyListener(27, function(){
37849            if(active.length > 0){
37850                hideAll();
37851            }
37852        });
37853    }
37854
37855    // private
37856    function hideAll(){
37857        if(active && active.length > 0){
37858            var c = active.clone();
37859            c.each(function(m){
37860                m.hide();
37861            });
37862        }
37863    }
37864
37865    // private
37866    function onHide(m){
37867        active.remove(m);
37868        if(active.length < 1){
37869            Roo.get(document).un("mousedown", onMouseDown);
37870            attached = false;
37871        }
37872    }
37873
37874    // private
37875    function onShow(m){
37876        var last = active.last();
37877        lastShow = new Date();
37878        active.add(m);
37879        if(!attached){
37880            Roo.get(document).on("mousedown", onMouseDown);
37881            attached = true;
37882        }
37883        if(m.parentMenu){
37884           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37885           m.parentMenu.activeChild = m;
37886        }else if(last && last.isVisible()){
37887           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37888        }
37889    }
37890
37891    // private
37892    function onBeforeHide(m){
37893        if(m.activeChild){
37894            m.activeChild.hide();
37895        }
37896        if(m.autoHideTimer){
37897            clearTimeout(m.autoHideTimer);
37898            delete m.autoHideTimer;
37899        }
37900    }
37901
37902    // private
37903    function onBeforeShow(m){
37904        var pm = m.parentMenu;
37905        if(!pm && !m.allowOtherMenus){
37906            hideAll();
37907        }else if(pm && pm.activeChild && active != m){
37908            pm.activeChild.hide();
37909        }
37910    }
37911
37912    // private
37913    function onMouseDown(e){
37914        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37915            hideAll();
37916        }
37917    }
37918
37919    // private
37920    function onBeforeCheck(mi, state){
37921        if(state){
37922            var g = groups[mi.group];
37923            for(var i = 0, l = g.length; i < l; i++){
37924                if(g[i] != mi){
37925                    g[i].setChecked(false);
37926                }
37927            }
37928        }
37929    }
37930
37931    return {
37932
37933        /**
37934         * Hides all menus that are currently visible
37935         */
37936        hideAll : function(){
37937             hideAll();  
37938        },
37939
37940        // private
37941        register : function(menu){
37942            if(!menus){
37943                init();
37944            }
37945            menus[menu.id] = menu;
37946            menu.on("beforehide", onBeforeHide);
37947            menu.on("hide", onHide);
37948            menu.on("beforeshow", onBeforeShow);
37949            menu.on("show", onShow);
37950            var g = menu.group;
37951            if(g && menu.events["checkchange"]){
37952                if(!groups[g]){
37953                    groups[g] = [];
37954                }
37955                groups[g].push(menu);
37956                menu.on("checkchange", onCheck);
37957            }
37958        },
37959
37960         /**
37961          * Returns a {@link Roo.menu.Menu} object
37962          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37963          * be used to generate and return a new Menu instance.
37964          */
37965        get : function(menu){
37966            if(typeof menu == "string"){ // menu id
37967                return menus[menu];
37968            }else if(menu.events){  // menu instance
37969                return menu;
37970            }else if(typeof menu.length == 'number'){ // array of menu items?
37971                return new Roo.menu.Menu({items:menu});
37972            }else{ // otherwise, must be a config
37973                return new Roo.menu.Menu(menu);
37974            }
37975        },
37976
37977        // private
37978        unregister : function(menu){
37979            delete menus[menu.id];
37980            menu.un("beforehide", onBeforeHide);
37981            menu.un("hide", onHide);
37982            menu.un("beforeshow", onBeforeShow);
37983            menu.un("show", onShow);
37984            var g = menu.group;
37985            if(g && menu.events["checkchange"]){
37986                groups[g].remove(menu);
37987                menu.un("checkchange", onCheck);
37988            }
37989        },
37990
37991        // private
37992        registerCheckable : function(menuItem){
37993            var g = menuItem.group;
37994            if(g){
37995                if(!groups[g]){
37996                    groups[g] = [];
37997                }
37998                groups[g].push(menuItem);
37999                menuItem.on("beforecheckchange", onBeforeCheck);
38000            }
38001        },
38002
38003        // private
38004        unregisterCheckable : function(menuItem){
38005            var g = menuItem.group;
38006            if(g){
38007                groups[g].remove(menuItem);
38008                menuItem.un("beforecheckchange", onBeforeCheck);
38009            }
38010        }
38011    };
38012 }();/*
38013  * Based on:
38014  * Ext JS Library 1.1.1
38015  * Copyright(c) 2006-2007, Ext JS, LLC.
38016  *
38017  * Originally Released Under LGPL - original licence link has changed is not relivant.
38018  *
38019  * Fork - LGPL
38020  * <script type="text/javascript">
38021  */
38022  
38023
38024 /**
38025  * @class Roo.menu.BaseItem
38026  * @extends Roo.Component
38027  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38028  * management and base configuration options shared by all menu components.
38029  * @constructor
38030  * Creates a new BaseItem
38031  * @param {Object} config Configuration options
38032  */
38033 Roo.menu.BaseItem = function(config){
38034     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38035
38036     this.addEvents({
38037         /**
38038          * @event click
38039          * Fires when this item is clicked
38040          * @param {Roo.menu.BaseItem} this
38041          * @param {Roo.EventObject} e
38042          */
38043         click: true,
38044         /**
38045          * @event activate
38046          * Fires when this item is activated
38047          * @param {Roo.menu.BaseItem} this
38048          */
38049         activate : true,
38050         /**
38051          * @event deactivate
38052          * Fires when this item is deactivated
38053          * @param {Roo.menu.BaseItem} this
38054          */
38055         deactivate : true
38056     });
38057
38058     if(this.handler){
38059         this.on("click", this.handler, this.scope, true);
38060     }
38061 };
38062
38063 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38064     /**
38065      * @cfg {Function} handler
38066      * A function that will handle the click event of this menu item (defaults to undefined)
38067      */
38068     /**
38069      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38070      */
38071     canActivate : false,
38072     
38073      /**
38074      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38075      */
38076     hidden: false,
38077     
38078     /**
38079      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38080      */
38081     activeClass : "x-menu-item-active",
38082     /**
38083      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38084      */
38085     hideOnClick : true,
38086     /**
38087      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38088      */
38089     hideDelay : 100,
38090
38091     // private
38092     ctype: "Roo.menu.BaseItem",
38093
38094     // private
38095     actionMode : "container",
38096
38097     // private
38098     render : function(container, parentMenu){
38099         this.parentMenu = parentMenu;
38100         Roo.menu.BaseItem.superclass.render.call(this, container);
38101         this.container.menuItemId = this.id;
38102     },
38103
38104     // private
38105     onRender : function(container, position){
38106         this.el = Roo.get(this.el);
38107         container.dom.appendChild(this.el.dom);
38108     },
38109
38110     // private
38111     onClick : function(e){
38112         if(!this.disabled && this.fireEvent("click", this, e) !== false
38113                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38114             this.handleClick(e);
38115         }else{
38116             e.stopEvent();
38117         }
38118     },
38119
38120     // private
38121     activate : function(){
38122         if(this.disabled){
38123             return false;
38124         }
38125         var li = this.container;
38126         li.addClass(this.activeClass);
38127         this.region = li.getRegion().adjust(2, 2, -2, -2);
38128         this.fireEvent("activate", this);
38129         return true;
38130     },
38131
38132     // private
38133     deactivate : function(){
38134         this.container.removeClass(this.activeClass);
38135         this.fireEvent("deactivate", this);
38136     },
38137
38138     // private
38139     shouldDeactivate : function(e){
38140         return !this.region || !this.region.contains(e.getPoint());
38141     },
38142
38143     // private
38144     handleClick : function(e){
38145         if(this.hideOnClick){
38146             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38147         }
38148     },
38149
38150     // private
38151     expandMenu : function(autoActivate){
38152         // do nothing
38153     },
38154
38155     // private
38156     hideMenu : function(){
38157         // do nothing
38158     }
38159 });/*
38160  * Based on:
38161  * Ext JS Library 1.1.1
38162  * Copyright(c) 2006-2007, Ext JS, LLC.
38163  *
38164  * Originally Released Under LGPL - original licence link has changed is not relivant.
38165  *
38166  * Fork - LGPL
38167  * <script type="text/javascript">
38168  */
38169  
38170 /**
38171  * @class Roo.menu.Adapter
38172  * @extends Roo.menu.BaseItem
38173  * 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.
38174  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38175  * @constructor
38176  * Creates a new Adapter
38177  * @param {Object} config Configuration options
38178  */
38179 Roo.menu.Adapter = function(component, config){
38180     Roo.menu.Adapter.superclass.constructor.call(this, config);
38181     this.component = component;
38182 };
38183 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38184     // private
38185     canActivate : true,
38186
38187     // private
38188     onRender : function(container, position){
38189         this.component.render(container);
38190         this.el = this.component.getEl();
38191     },
38192
38193     // private
38194     activate : function(){
38195         if(this.disabled){
38196             return false;
38197         }
38198         this.component.focus();
38199         this.fireEvent("activate", this);
38200         return true;
38201     },
38202
38203     // private
38204     deactivate : function(){
38205         this.fireEvent("deactivate", this);
38206     },
38207
38208     // private
38209     disable : function(){
38210         this.component.disable();
38211         Roo.menu.Adapter.superclass.disable.call(this);
38212     },
38213
38214     // private
38215     enable : function(){
38216         this.component.enable();
38217         Roo.menu.Adapter.superclass.enable.call(this);
38218     }
38219 });/*
38220  * Based on:
38221  * Ext JS Library 1.1.1
38222  * Copyright(c) 2006-2007, Ext JS, LLC.
38223  *
38224  * Originally Released Under LGPL - original licence link has changed is not relivant.
38225  *
38226  * Fork - LGPL
38227  * <script type="text/javascript">
38228  */
38229
38230 /**
38231  * @class Roo.menu.TextItem
38232  * @extends Roo.menu.BaseItem
38233  * Adds a static text string to a menu, usually used as either a heading or group separator.
38234  * Note: old style constructor with text is still supported.
38235  * 
38236  * @constructor
38237  * Creates a new TextItem
38238  * @param {Object} cfg Configuration
38239  */
38240 Roo.menu.TextItem = function(cfg){
38241     if (typeof(cfg) == 'string') {
38242         this.text = cfg;
38243     } else {
38244         Roo.apply(this,cfg);
38245     }
38246     
38247     Roo.menu.TextItem.superclass.constructor.call(this);
38248 };
38249
38250 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38251     /**
38252      * @cfg {Boolean} text Text to show on item.
38253      */
38254     text : '',
38255     
38256     /**
38257      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38258      */
38259     hideOnClick : false,
38260     /**
38261      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38262      */
38263     itemCls : "x-menu-text",
38264
38265     // private
38266     onRender : function(){
38267         var s = document.createElement("span");
38268         s.className = this.itemCls;
38269         s.innerHTML = this.text;
38270         this.el = s;
38271         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38272     }
38273 });/*
38274  * Based on:
38275  * Ext JS Library 1.1.1
38276  * Copyright(c) 2006-2007, Ext JS, LLC.
38277  *
38278  * Originally Released Under LGPL - original licence link has changed is not relivant.
38279  *
38280  * Fork - LGPL
38281  * <script type="text/javascript">
38282  */
38283
38284 /**
38285  * @class Roo.menu.Separator
38286  * @extends Roo.menu.BaseItem
38287  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38288  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38289  * @constructor
38290  * @param {Object} config Configuration options
38291  */
38292 Roo.menu.Separator = function(config){
38293     Roo.menu.Separator.superclass.constructor.call(this, config);
38294 };
38295
38296 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38297     /**
38298      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38299      */
38300     itemCls : "x-menu-sep",
38301     /**
38302      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38303      */
38304     hideOnClick : false,
38305
38306     // private
38307     onRender : function(li){
38308         var s = document.createElement("span");
38309         s.className = this.itemCls;
38310         s.innerHTML = "&#160;";
38311         this.el = s;
38312         li.addClass("x-menu-sep-li");
38313         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38314     }
38315 });/*
38316  * Based on:
38317  * Ext JS Library 1.1.1
38318  * Copyright(c) 2006-2007, Ext JS, LLC.
38319  *
38320  * Originally Released Under LGPL - original licence link has changed is not relivant.
38321  *
38322  * Fork - LGPL
38323  * <script type="text/javascript">
38324  */
38325 /**
38326  * @class Roo.menu.Item
38327  * @extends Roo.menu.BaseItem
38328  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38329  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38330  * activation and click handling.
38331  * @constructor
38332  * Creates a new Item
38333  * @param {Object} config Configuration options
38334  */
38335 Roo.menu.Item = function(config){
38336     Roo.menu.Item.superclass.constructor.call(this, config);
38337     if(this.menu){
38338         this.menu = Roo.menu.MenuMgr.get(this.menu);
38339     }
38340 };
38341 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38342     
38343     /**
38344      * @cfg {String} text
38345      * The text to show on the menu item.
38346      */
38347     text: '',
38348      /**
38349      * @cfg {String} HTML to render in menu
38350      * The text to show on the menu item (HTML version).
38351      */
38352     html: '',
38353     /**
38354      * @cfg {String} icon
38355      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38356      */
38357     icon: undefined,
38358     /**
38359      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38360      */
38361     itemCls : "x-menu-item",
38362     /**
38363      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38364      */
38365     canActivate : true,
38366     /**
38367      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38368      */
38369     showDelay: 200,
38370     // doc'd in BaseItem
38371     hideDelay: 200,
38372
38373     // private
38374     ctype: "Roo.menu.Item",
38375     
38376     // private
38377     onRender : function(container, position){
38378         var el = document.createElement("a");
38379         el.hideFocus = true;
38380         el.unselectable = "on";
38381         el.href = this.href || "#";
38382         if(this.hrefTarget){
38383             el.target = this.hrefTarget;
38384         }
38385         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38386         
38387         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38388         
38389         el.innerHTML = String.format(
38390                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38391                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38392         this.el = el;
38393         Roo.menu.Item.superclass.onRender.call(this, container, position);
38394     },
38395
38396     /**
38397      * Sets the text to display in this menu item
38398      * @param {String} text The text to display
38399      * @param {Boolean} isHTML true to indicate text is pure html.
38400      */
38401     setText : function(text, isHTML){
38402         if (isHTML) {
38403             this.html = text;
38404         } else {
38405             this.text = text;
38406             this.html = '';
38407         }
38408         if(this.rendered){
38409             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38410      
38411             this.el.update(String.format(
38412                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38413                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38414             this.parentMenu.autoWidth();
38415         }
38416     },
38417
38418     // private
38419     handleClick : function(e){
38420         if(!this.href){ // if no link defined, stop the event automatically
38421             e.stopEvent();
38422         }
38423         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38424     },
38425
38426     // private
38427     activate : function(autoExpand){
38428         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38429             this.focus();
38430             if(autoExpand){
38431                 this.expandMenu();
38432             }
38433         }
38434         return true;
38435     },
38436
38437     // private
38438     shouldDeactivate : function(e){
38439         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38440             if(this.menu && this.menu.isVisible()){
38441                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38442             }
38443             return true;
38444         }
38445         return false;
38446     },
38447
38448     // private
38449     deactivate : function(){
38450         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38451         this.hideMenu();
38452     },
38453
38454     // private
38455     expandMenu : function(autoActivate){
38456         if(!this.disabled && this.menu){
38457             clearTimeout(this.hideTimer);
38458             delete this.hideTimer;
38459             if(!this.menu.isVisible() && !this.showTimer){
38460                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38461             }else if (this.menu.isVisible() && autoActivate){
38462                 this.menu.tryActivate(0, 1);
38463             }
38464         }
38465     },
38466
38467     // private
38468     deferExpand : function(autoActivate){
38469         delete this.showTimer;
38470         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38471         if(autoActivate){
38472             this.menu.tryActivate(0, 1);
38473         }
38474     },
38475
38476     // private
38477     hideMenu : function(){
38478         clearTimeout(this.showTimer);
38479         delete this.showTimer;
38480         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38481             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38482         }
38483     },
38484
38485     // private
38486     deferHide : function(){
38487         delete this.hideTimer;
38488         this.menu.hide();
38489     }
38490 });/*
38491  * Based on:
38492  * Ext JS Library 1.1.1
38493  * Copyright(c) 2006-2007, Ext JS, LLC.
38494  *
38495  * Originally Released Under LGPL - original licence link has changed is not relivant.
38496  *
38497  * Fork - LGPL
38498  * <script type="text/javascript">
38499  */
38500  
38501 /**
38502  * @class Roo.menu.CheckItem
38503  * @extends Roo.menu.Item
38504  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38505  * @constructor
38506  * Creates a new CheckItem
38507  * @param {Object} config Configuration options
38508  */
38509 Roo.menu.CheckItem = function(config){
38510     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38511     this.addEvents({
38512         /**
38513          * @event beforecheckchange
38514          * Fires before the checked value is set, providing an opportunity to cancel if needed
38515          * @param {Roo.menu.CheckItem} this
38516          * @param {Boolean} checked The new checked value that will be set
38517          */
38518         "beforecheckchange" : true,
38519         /**
38520          * @event checkchange
38521          * Fires after the checked value has been set
38522          * @param {Roo.menu.CheckItem} this
38523          * @param {Boolean} checked The checked value that was set
38524          */
38525         "checkchange" : true
38526     });
38527     if(this.checkHandler){
38528         this.on('checkchange', this.checkHandler, this.scope);
38529     }
38530 };
38531 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38532     /**
38533      * @cfg {String} group
38534      * All check items with the same group name will automatically be grouped into a single-select
38535      * radio button group (defaults to '')
38536      */
38537     /**
38538      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38539      */
38540     itemCls : "x-menu-item x-menu-check-item",
38541     /**
38542      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38543      */
38544     groupClass : "x-menu-group-item",
38545
38546     /**
38547      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38548      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38549      * initialized with checked = true will be rendered as checked.
38550      */
38551     checked: false,
38552
38553     // private
38554     ctype: "Roo.menu.CheckItem",
38555
38556     // private
38557     onRender : function(c){
38558         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38559         if(this.group){
38560             this.el.addClass(this.groupClass);
38561         }
38562         Roo.menu.MenuMgr.registerCheckable(this);
38563         if(this.checked){
38564             this.checked = false;
38565             this.setChecked(true, true);
38566         }
38567     },
38568
38569     // private
38570     destroy : function(){
38571         if(this.rendered){
38572             Roo.menu.MenuMgr.unregisterCheckable(this);
38573         }
38574         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38575     },
38576
38577     /**
38578      * Set the checked state of this item
38579      * @param {Boolean} checked The new checked value
38580      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38581      */
38582     setChecked : function(state, suppressEvent){
38583         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38584             if(this.container){
38585                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38586             }
38587             this.checked = state;
38588             if(suppressEvent !== true){
38589                 this.fireEvent("checkchange", this, state);
38590             }
38591         }
38592     },
38593
38594     // private
38595     handleClick : function(e){
38596        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38597            this.setChecked(!this.checked);
38598        }
38599        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38600     }
38601 });/*
38602  * Based on:
38603  * Ext JS Library 1.1.1
38604  * Copyright(c) 2006-2007, Ext JS, LLC.
38605  *
38606  * Originally Released Under LGPL - original licence link has changed is not relivant.
38607  *
38608  * Fork - LGPL
38609  * <script type="text/javascript">
38610  */
38611  
38612 /**
38613  * @class Roo.menu.DateItem
38614  * @extends Roo.menu.Adapter
38615  * A menu item that wraps the {@link Roo.DatPicker} component.
38616  * @constructor
38617  * Creates a new DateItem
38618  * @param {Object} config Configuration options
38619  */
38620 Roo.menu.DateItem = function(config){
38621     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38622     /** The Roo.DatePicker object @type Roo.DatePicker */
38623     this.picker = this.component;
38624     this.addEvents({select: true});
38625     
38626     this.picker.on("render", function(picker){
38627         picker.getEl().swallowEvent("click");
38628         picker.container.addClass("x-menu-date-item");
38629     });
38630
38631     this.picker.on("select", this.onSelect, this);
38632 };
38633
38634 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38635     // private
38636     onSelect : function(picker, date){
38637         this.fireEvent("select", this, date, picker);
38638         Roo.menu.DateItem.superclass.handleClick.call(this);
38639     }
38640 });/*
38641  * Based on:
38642  * Ext JS Library 1.1.1
38643  * Copyright(c) 2006-2007, Ext JS, LLC.
38644  *
38645  * Originally Released Under LGPL - original licence link has changed is not relivant.
38646  *
38647  * Fork - LGPL
38648  * <script type="text/javascript">
38649  */
38650  
38651 /**
38652  * @class Roo.menu.ColorItem
38653  * @extends Roo.menu.Adapter
38654  * A menu item that wraps the {@link Roo.ColorPalette} component.
38655  * @constructor
38656  * Creates a new ColorItem
38657  * @param {Object} config Configuration options
38658  */
38659 Roo.menu.ColorItem = function(config){
38660     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38661     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38662     this.palette = this.component;
38663     this.relayEvents(this.palette, ["select"]);
38664     if(this.selectHandler){
38665         this.on('select', this.selectHandler, this.scope);
38666     }
38667 };
38668 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38669  * Based on:
38670  * Ext JS Library 1.1.1
38671  * Copyright(c) 2006-2007, Ext JS, LLC.
38672  *
38673  * Originally Released Under LGPL - original licence link has changed is not relivant.
38674  *
38675  * Fork - LGPL
38676  * <script type="text/javascript">
38677  */
38678  
38679
38680 /**
38681  * @class Roo.menu.DateMenu
38682  * @extends Roo.menu.Menu
38683  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38684  * @constructor
38685  * Creates a new DateMenu
38686  * @param {Object} config Configuration options
38687  */
38688 Roo.menu.DateMenu = function(config){
38689     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38690     this.plain = true;
38691     var di = new Roo.menu.DateItem(config);
38692     this.add(di);
38693     /**
38694      * The {@link Roo.DatePicker} instance for this DateMenu
38695      * @type DatePicker
38696      */
38697     this.picker = di.picker;
38698     /**
38699      * @event select
38700      * @param {DatePicker} picker
38701      * @param {Date} date
38702      */
38703     this.relayEvents(di, ["select"]);
38704     this.on('beforeshow', function(){
38705         if(this.picker){
38706             this.picker.hideMonthPicker(false);
38707         }
38708     }, this);
38709 };
38710 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38711     cls:'x-date-menu'
38712 });/*
38713  * Based on:
38714  * Ext JS Library 1.1.1
38715  * Copyright(c) 2006-2007, Ext JS, LLC.
38716  *
38717  * Originally Released Under LGPL - original licence link has changed is not relivant.
38718  *
38719  * Fork - LGPL
38720  * <script type="text/javascript">
38721  */
38722  
38723
38724 /**
38725  * @class Roo.menu.ColorMenu
38726  * @extends Roo.menu.Menu
38727  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38728  * @constructor
38729  * Creates a new ColorMenu
38730  * @param {Object} config Configuration options
38731  */
38732 Roo.menu.ColorMenu = function(config){
38733     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38734     this.plain = true;
38735     var ci = new Roo.menu.ColorItem(config);
38736     this.add(ci);
38737     /**
38738      * The {@link Roo.ColorPalette} instance for this ColorMenu
38739      * @type ColorPalette
38740      */
38741     this.palette = ci.palette;
38742     /**
38743      * @event select
38744      * @param {ColorPalette} palette
38745      * @param {String} color
38746      */
38747     this.relayEvents(ci, ["select"]);
38748 };
38749 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38750  * Based on:
38751  * Ext JS Library 1.1.1
38752  * Copyright(c) 2006-2007, Ext JS, LLC.
38753  *
38754  * Originally Released Under LGPL - original licence link has changed is not relivant.
38755  *
38756  * Fork - LGPL
38757  * <script type="text/javascript">
38758  */
38759  
38760 /**
38761  * @class Roo.form.TextItem
38762  * @extends Roo.BoxComponent
38763  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38764  * @constructor
38765  * Creates a new TextItem
38766  * @param {Object} config Configuration options
38767  */
38768 Roo.form.TextItem = function(config){
38769     Roo.form.TextItem.superclass.constructor.call(this, config);
38770 };
38771
38772 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
38773     
38774     /**
38775      * @cfg {String} tag the tag for this item (default div)
38776      */
38777     tag : 'div',
38778     /**
38779      * @cfg {String} html the content for this item
38780      */
38781     html : '',
38782     
38783     getAutoCreate : function()
38784     {
38785         var cfg = {
38786             id: this.id,
38787             tag: this.tag,
38788             html: this.html,
38789             cls: 'x-form-item'
38790         };
38791         
38792         return cfg;
38793         
38794     },
38795     
38796     onRender : function(ct, position)
38797     {
38798         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
38799         
38800         if(!this.el){
38801             var cfg = this.getAutoCreate();
38802             if(!cfg.name){
38803                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38804             }
38805             if (!cfg.name.length) {
38806                 delete cfg.name;
38807             }
38808             this.el = ct.createChild(cfg, position);
38809         }
38810     }
38811     
38812 });/*
38813  * Based on:
38814  * Ext JS Library 1.1.1
38815  * Copyright(c) 2006-2007, Ext JS, LLC.
38816  *
38817  * Originally Released Under LGPL - original licence link has changed is not relivant.
38818  *
38819  * Fork - LGPL
38820  * <script type="text/javascript">
38821  */
38822  
38823 /**
38824  * @class Roo.form.Field
38825  * @extends Roo.BoxComponent
38826  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38827  * @constructor
38828  * Creates a new Field
38829  * @param {Object} config Configuration options
38830  */
38831 Roo.form.Field = function(config){
38832     Roo.form.Field.superclass.constructor.call(this, config);
38833 };
38834
38835 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38836     /**
38837      * @cfg {String} fieldLabel Label to use when rendering a form.
38838      */
38839        /**
38840      * @cfg {String} qtip Mouse over tip
38841      */
38842      
38843     /**
38844      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38845      */
38846     invalidClass : "x-form-invalid",
38847     /**
38848      * @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")
38849      */
38850     invalidText : "The value in this field is invalid",
38851     /**
38852      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38853      */
38854     focusClass : "x-form-focus",
38855     /**
38856      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38857       automatic validation (defaults to "keyup").
38858      */
38859     validationEvent : "keyup",
38860     /**
38861      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38862      */
38863     validateOnBlur : true,
38864     /**
38865      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38866      */
38867     validationDelay : 250,
38868     /**
38869      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38870      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38871      */
38872     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38873     /**
38874      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38875      */
38876     fieldClass : "x-form-field",
38877     /**
38878      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38879      *<pre>
38880 Value         Description
38881 -----------   ----------------------------------------------------------------------
38882 qtip          Display a quick tip when the user hovers over the field
38883 title         Display a default browser title attribute popup
38884 under         Add a block div beneath the field containing the error text
38885 side          Add an error icon to the right of the field with a popup on hover
38886 [element id]  Add the error text directly to the innerHTML of the specified element
38887 </pre>
38888      */
38889     msgTarget : 'qtip',
38890     /**
38891      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38892      */
38893     msgFx : 'normal',
38894
38895     /**
38896      * @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.
38897      */
38898     readOnly : false,
38899
38900     /**
38901      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38902      */
38903     disabled : false,
38904
38905     /**
38906      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38907      */
38908     inputType : undefined,
38909     
38910     /**
38911      * @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).
38912          */
38913         tabIndex : undefined,
38914         
38915     // private
38916     isFormField : true,
38917
38918     // private
38919     hasFocus : false,
38920     /**
38921      * @property {Roo.Element} fieldEl
38922      * Element Containing the rendered Field (with label etc.)
38923      */
38924     /**
38925      * @cfg {Mixed} value A value to initialize this field with.
38926      */
38927     value : undefined,
38928
38929     /**
38930      * @cfg {String} name The field's HTML name attribute.
38931      */
38932     /**
38933      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38934      */
38935     // private
38936     loadedValue : false,
38937      
38938      
38939         // private ??
38940         initComponent : function(){
38941         Roo.form.Field.superclass.initComponent.call(this);
38942         this.addEvents({
38943             /**
38944              * @event focus
38945              * Fires when this field receives input focus.
38946              * @param {Roo.form.Field} this
38947              */
38948             focus : true,
38949             /**
38950              * @event blur
38951              * Fires when this field loses input focus.
38952              * @param {Roo.form.Field} this
38953              */
38954             blur : true,
38955             /**
38956              * @event specialkey
38957              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38958              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38959              * @param {Roo.form.Field} this
38960              * @param {Roo.EventObject} e The event object
38961              */
38962             specialkey : true,
38963             /**
38964              * @event change
38965              * Fires just before the field blurs if the field value has changed.
38966              * @param {Roo.form.Field} this
38967              * @param {Mixed} newValue The new value
38968              * @param {Mixed} oldValue The original value
38969              */
38970             change : true,
38971             /**
38972              * @event invalid
38973              * Fires after the field has been marked as invalid.
38974              * @param {Roo.form.Field} this
38975              * @param {String} msg The validation message
38976              */
38977             invalid : true,
38978             /**
38979              * @event valid
38980              * Fires after the field has been validated with no errors.
38981              * @param {Roo.form.Field} this
38982              */
38983             valid : true,
38984              /**
38985              * @event keyup
38986              * Fires after the key up
38987              * @param {Roo.form.Field} this
38988              * @param {Roo.EventObject}  e The event Object
38989              */
38990             keyup : true
38991         });
38992     },
38993
38994     /**
38995      * Returns the name attribute of the field if available
38996      * @return {String} name The field name
38997      */
38998     getName: function(){
38999          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39000     },
39001
39002     // private
39003     onRender : function(ct, position){
39004         Roo.form.Field.superclass.onRender.call(this, ct, position);
39005         if(!this.el){
39006             var cfg = this.getAutoCreate();
39007             if(!cfg.name){
39008                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39009             }
39010             if (!cfg.name.length) {
39011                 delete cfg.name;
39012             }
39013             if(this.inputType){
39014                 cfg.type = this.inputType;
39015             }
39016             this.el = ct.createChild(cfg, position);
39017         }
39018         var type = this.el.dom.type;
39019         if(type){
39020             if(type == 'password'){
39021                 type = 'text';
39022             }
39023             this.el.addClass('x-form-'+type);
39024         }
39025         if(this.readOnly){
39026             this.el.dom.readOnly = true;
39027         }
39028         if(this.tabIndex !== undefined){
39029             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39030         }
39031
39032         this.el.addClass([this.fieldClass, this.cls]);
39033         this.initValue();
39034     },
39035
39036     /**
39037      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39038      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39039      * @return {Roo.form.Field} this
39040      */
39041     applyTo : function(target){
39042         this.allowDomMove = false;
39043         this.el = Roo.get(target);
39044         this.render(this.el.dom.parentNode);
39045         return this;
39046     },
39047
39048     // private
39049     initValue : function(){
39050         if(this.value !== undefined){
39051             this.setValue(this.value);
39052         }else if(this.el.dom.value.length > 0){
39053             this.setValue(this.el.dom.value);
39054         }
39055     },
39056
39057     /**
39058      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39059      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39060      */
39061     isDirty : function() {
39062         if(this.disabled) {
39063             return false;
39064         }
39065         return String(this.getValue()) !== String(this.originalValue);
39066     },
39067
39068     /**
39069      * stores the current value in loadedValue
39070      */
39071     resetHasChanged : function()
39072     {
39073         this.loadedValue = String(this.getValue());
39074     },
39075     /**
39076      * checks the current value against the 'loaded' value.
39077      * Note - will return false if 'resetHasChanged' has not been called first.
39078      */
39079     hasChanged : function()
39080     {
39081         if(this.disabled || this.readOnly) {
39082             return false;
39083         }
39084         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39085     },
39086     
39087     
39088     
39089     // private
39090     afterRender : function(){
39091         Roo.form.Field.superclass.afterRender.call(this);
39092         this.initEvents();
39093     },
39094
39095     // private
39096     fireKey : function(e){
39097         //Roo.log('field ' + e.getKey());
39098         if(e.isNavKeyPress()){
39099             this.fireEvent("specialkey", this, e);
39100         }
39101     },
39102
39103     /**
39104      * Resets the current field value to the originally loaded value and clears any validation messages
39105      */
39106     reset : function(){
39107         this.setValue(this.resetValue);
39108         this.originalValue = this.getValue();
39109         this.clearInvalid();
39110     },
39111
39112     // private
39113     initEvents : function(){
39114         // safari killled keypress - so keydown is now used..
39115         this.el.on("keydown" , this.fireKey,  this);
39116         this.el.on("focus", this.onFocus,  this);
39117         this.el.on("blur", this.onBlur,  this);
39118         this.el.relayEvent('keyup', this);
39119
39120         // reference to original value for reset
39121         this.originalValue = this.getValue();
39122         this.resetValue =  this.getValue();
39123     },
39124
39125     // private
39126     onFocus : function(){
39127         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39128             this.el.addClass(this.focusClass);
39129         }
39130         if(!this.hasFocus){
39131             this.hasFocus = true;
39132             this.startValue = this.getValue();
39133             this.fireEvent("focus", this);
39134         }
39135     },
39136
39137     beforeBlur : Roo.emptyFn,
39138
39139     // private
39140     onBlur : function(){
39141         this.beforeBlur();
39142         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39143             this.el.removeClass(this.focusClass);
39144         }
39145         this.hasFocus = false;
39146         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39147             this.validate();
39148         }
39149         var v = this.getValue();
39150         if(String(v) !== String(this.startValue)){
39151             this.fireEvent('change', this, v, this.startValue);
39152         }
39153         this.fireEvent("blur", this);
39154     },
39155
39156     /**
39157      * Returns whether or not the field value is currently valid
39158      * @param {Boolean} preventMark True to disable marking the field invalid
39159      * @return {Boolean} True if the value is valid, else false
39160      */
39161     isValid : function(preventMark){
39162         if(this.disabled){
39163             return true;
39164         }
39165         var restore = this.preventMark;
39166         this.preventMark = preventMark === true;
39167         var v = this.validateValue(this.processValue(this.getRawValue()));
39168         this.preventMark = restore;
39169         return v;
39170     },
39171
39172     /**
39173      * Validates the field value
39174      * @return {Boolean} True if the value is valid, else false
39175      */
39176     validate : function(){
39177         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39178             this.clearInvalid();
39179             return true;
39180         }
39181         return false;
39182     },
39183
39184     processValue : function(value){
39185         return value;
39186     },
39187
39188     // private
39189     // Subclasses should provide the validation implementation by overriding this
39190     validateValue : function(value){
39191         return true;
39192     },
39193
39194     /**
39195      * Mark this field as invalid
39196      * @param {String} msg The validation message
39197      */
39198     markInvalid : function(msg){
39199         if(!this.rendered || this.preventMark){ // not rendered
39200             return;
39201         }
39202         
39203         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39204         
39205         obj.el.addClass(this.invalidClass);
39206         msg = msg || this.invalidText;
39207         switch(this.msgTarget){
39208             case 'qtip':
39209                 obj.el.dom.qtip = msg;
39210                 obj.el.dom.qclass = 'x-form-invalid-tip';
39211                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39212                     Roo.QuickTips.enable();
39213                 }
39214                 break;
39215             case 'title':
39216                 this.el.dom.title = msg;
39217                 break;
39218             case 'under':
39219                 if(!this.errorEl){
39220                     var elp = this.el.findParent('.x-form-element', 5, true);
39221                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39222                     this.errorEl.setWidth(elp.getWidth(true)-20);
39223                 }
39224                 this.errorEl.update(msg);
39225                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39226                 break;
39227             case 'side':
39228                 if(!this.errorIcon){
39229                     var elp = this.el.findParent('.x-form-element', 5, true);
39230                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39231                 }
39232                 this.alignErrorIcon();
39233                 this.errorIcon.dom.qtip = msg;
39234                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39235                 this.errorIcon.show();
39236                 this.on('resize', this.alignErrorIcon, this);
39237                 break;
39238             default:
39239                 var t = Roo.getDom(this.msgTarget);
39240                 t.innerHTML = msg;
39241                 t.style.display = this.msgDisplay;
39242                 break;
39243         }
39244         this.fireEvent('invalid', this, msg);
39245     },
39246
39247     // private
39248     alignErrorIcon : function(){
39249         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39250     },
39251
39252     /**
39253      * Clear any invalid styles/messages for this field
39254      */
39255     clearInvalid : function(){
39256         if(!this.rendered || this.preventMark){ // not rendered
39257             return;
39258         }
39259         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39260         
39261         obj.el.removeClass(this.invalidClass);
39262         switch(this.msgTarget){
39263             case 'qtip':
39264                 obj.el.dom.qtip = '';
39265                 break;
39266             case 'title':
39267                 this.el.dom.title = '';
39268                 break;
39269             case 'under':
39270                 if(this.errorEl){
39271                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39272                 }
39273                 break;
39274             case 'side':
39275                 if(this.errorIcon){
39276                     this.errorIcon.dom.qtip = '';
39277                     this.errorIcon.hide();
39278                     this.un('resize', this.alignErrorIcon, this);
39279                 }
39280                 break;
39281             default:
39282                 var t = Roo.getDom(this.msgTarget);
39283                 t.innerHTML = '';
39284                 t.style.display = 'none';
39285                 break;
39286         }
39287         this.fireEvent('valid', this);
39288     },
39289
39290     /**
39291      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39292      * @return {Mixed} value The field value
39293      */
39294     getRawValue : function(){
39295         var v = this.el.getValue();
39296         
39297         return v;
39298     },
39299
39300     /**
39301      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39302      * @return {Mixed} value The field value
39303      */
39304     getValue : function(){
39305         var v = this.el.getValue();
39306          
39307         return v;
39308     },
39309
39310     /**
39311      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39312      * @param {Mixed} value The value to set
39313      */
39314     setRawValue : function(v){
39315         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39316     },
39317
39318     /**
39319      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39320      * @param {Mixed} value The value to set
39321      */
39322     setValue : function(v){
39323         this.value = v;
39324         if(this.rendered){
39325             this.el.dom.value = (v === null || v === undefined ? '' : v);
39326              this.validate();
39327         }
39328     },
39329
39330     adjustSize : function(w, h){
39331         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39332         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39333         return s;
39334     },
39335
39336     adjustWidth : function(tag, w){
39337         tag = tag.toLowerCase();
39338         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39339             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39340                 if(tag == 'input'){
39341                     return w + 2;
39342                 }
39343                 if(tag == 'textarea'){
39344                     return w-2;
39345                 }
39346             }else if(Roo.isOpera){
39347                 if(tag == 'input'){
39348                     return w + 2;
39349                 }
39350                 if(tag == 'textarea'){
39351                     return w-2;
39352                 }
39353             }
39354         }
39355         return w;
39356     }
39357 });
39358
39359
39360 // anything other than normal should be considered experimental
39361 Roo.form.Field.msgFx = {
39362     normal : {
39363         show: function(msgEl, f){
39364             msgEl.setDisplayed('block');
39365         },
39366
39367         hide : function(msgEl, f){
39368             msgEl.setDisplayed(false).update('');
39369         }
39370     },
39371
39372     slide : {
39373         show: function(msgEl, f){
39374             msgEl.slideIn('t', {stopFx:true});
39375         },
39376
39377         hide : function(msgEl, f){
39378             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39379         }
39380     },
39381
39382     slideRight : {
39383         show: function(msgEl, f){
39384             msgEl.fixDisplay();
39385             msgEl.alignTo(f.el, 'tl-tr');
39386             msgEl.slideIn('l', {stopFx:true});
39387         },
39388
39389         hide : function(msgEl, f){
39390             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39391         }
39392     }
39393 };/*
39394  * Based on:
39395  * Ext JS Library 1.1.1
39396  * Copyright(c) 2006-2007, Ext JS, LLC.
39397  *
39398  * Originally Released Under LGPL - original licence link has changed is not relivant.
39399  *
39400  * Fork - LGPL
39401  * <script type="text/javascript">
39402  */
39403  
39404
39405 /**
39406  * @class Roo.form.TextField
39407  * @extends Roo.form.Field
39408  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39409  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39410  * @constructor
39411  * Creates a new TextField
39412  * @param {Object} config Configuration options
39413  */
39414 Roo.form.TextField = function(config){
39415     Roo.form.TextField.superclass.constructor.call(this, config);
39416     this.addEvents({
39417         /**
39418          * @event autosize
39419          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39420          * according to the default logic, but this event provides a hook for the developer to apply additional
39421          * logic at runtime to resize the field if needed.
39422              * @param {Roo.form.Field} this This text field
39423              * @param {Number} width The new field width
39424              */
39425         autosize : true
39426     });
39427 };
39428
39429 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39430     /**
39431      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39432      */
39433     grow : false,
39434     /**
39435      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39436      */
39437     growMin : 30,
39438     /**
39439      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39440      */
39441     growMax : 800,
39442     /**
39443      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39444      */
39445     vtype : null,
39446     /**
39447      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39448      */
39449     maskRe : null,
39450     /**
39451      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39452      */
39453     disableKeyFilter : false,
39454     /**
39455      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39456      */
39457     allowBlank : true,
39458     /**
39459      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39460      */
39461     minLength : 0,
39462     /**
39463      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39464      */
39465     maxLength : Number.MAX_VALUE,
39466     /**
39467      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39468      */
39469     minLengthText : "The minimum length for this field is {0}",
39470     /**
39471      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39472      */
39473     maxLengthText : "The maximum length for this field is {0}",
39474     /**
39475      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39476      */
39477     selectOnFocus : false,
39478     /**
39479      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39480      */    
39481     allowLeadingSpace : false,
39482     /**
39483      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39484      */
39485     blankText : "This field is required",
39486     /**
39487      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39488      * If available, this function will be called only after the basic validators all return true, and will be passed the
39489      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39490      */
39491     validator : null,
39492     /**
39493      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39494      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39495      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39496      */
39497     regex : null,
39498     /**
39499      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39500      */
39501     regexText : "",
39502     /**
39503      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39504      */
39505     emptyText : null,
39506    
39507
39508     // private
39509     initEvents : function()
39510     {
39511         if (this.emptyText) {
39512             this.el.attr('placeholder', this.emptyText);
39513         }
39514         
39515         Roo.form.TextField.superclass.initEvents.call(this);
39516         if(this.validationEvent == 'keyup'){
39517             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39518             this.el.on('keyup', this.filterValidation, this);
39519         }
39520         else if(this.validationEvent !== false){
39521             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39522         }
39523         
39524         if(this.selectOnFocus){
39525             this.on("focus", this.preFocus, this);
39526         }
39527         if (!this.allowLeadingSpace) {
39528             this.on('blur', this.cleanLeadingSpace, this);
39529         }
39530         
39531         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39532             this.el.on("keypress", this.filterKeys, this);
39533         }
39534         if(this.grow){
39535             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39536             this.el.on("click", this.autoSize,  this);
39537         }
39538         if(this.el.is('input[type=password]') && Roo.isSafari){
39539             this.el.on('keydown', this.SafariOnKeyDown, this);
39540         }
39541     },
39542
39543     processValue : function(value){
39544         if(this.stripCharsRe){
39545             var newValue = value.replace(this.stripCharsRe, '');
39546             if(newValue !== value){
39547                 this.setRawValue(newValue);
39548                 return newValue;
39549             }
39550         }
39551         return value;
39552     },
39553
39554     filterValidation : function(e){
39555         if(!e.isNavKeyPress()){
39556             this.validationTask.delay(this.validationDelay);
39557         }
39558     },
39559
39560     // private
39561     onKeyUp : function(e){
39562         if(!e.isNavKeyPress()){
39563             this.autoSize();
39564         }
39565     },
39566     // private - clean the leading white space
39567     cleanLeadingSpace : function(e)
39568     {
39569         if ( this.inputType == 'file') {
39570             return;
39571         }
39572         
39573         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39574     },
39575     /**
39576      * Resets the current field value to the originally-loaded value and clears any validation messages.
39577      *  
39578      */
39579     reset : function(){
39580         Roo.form.TextField.superclass.reset.call(this);
39581        
39582     }, 
39583     // private
39584     preFocus : function(){
39585         
39586         if(this.selectOnFocus){
39587             this.el.dom.select();
39588         }
39589     },
39590
39591     
39592     // private
39593     filterKeys : function(e){
39594         var k = e.getKey();
39595         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39596             return;
39597         }
39598         var c = e.getCharCode(), cc = String.fromCharCode(c);
39599         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39600             return;
39601         }
39602         if(!this.maskRe.test(cc)){
39603             e.stopEvent();
39604         }
39605     },
39606
39607     setValue : function(v){
39608         
39609         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39610         
39611         this.autoSize();
39612     },
39613
39614     /**
39615      * Validates a value according to the field's validation rules and marks the field as invalid
39616      * if the validation fails
39617      * @param {Mixed} value The value to validate
39618      * @return {Boolean} True if the value is valid, else false
39619      */
39620     validateValue : function(value){
39621         if(value.length < 1)  { // if it's blank
39622              if(this.allowBlank){
39623                 this.clearInvalid();
39624                 return true;
39625              }else{
39626                 this.markInvalid(this.blankText);
39627                 return false;
39628              }
39629         }
39630         if(value.length < this.minLength){
39631             this.markInvalid(String.format(this.minLengthText, this.minLength));
39632             return false;
39633         }
39634         if(value.length > this.maxLength){
39635             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39636             return false;
39637         }
39638         if(this.vtype){
39639             var vt = Roo.form.VTypes;
39640             if(!vt[this.vtype](value, this)){
39641                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39642                 return false;
39643             }
39644         }
39645         if(typeof this.validator == "function"){
39646             var msg = this.validator(value);
39647             if(msg !== true){
39648                 this.markInvalid(msg);
39649                 return false;
39650             }
39651         }
39652         if(this.regex && !this.regex.test(value)){
39653             this.markInvalid(this.regexText);
39654             return false;
39655         }
39656         return true;
39657     },
39658
39659     /**
39660      * Selects text in this field
39661      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39662      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39663      */
39664     selectText : function(start, end){
39665         var v = this.getRawValue();
39666         if(v.length > 0){
39667             start = start === undefined ? 0 : start;
39668             end = end === undefined ? v.length : end;
39669             var d = this.el.dom;
39670             if(d.setSelectionRange){
39671                 d.setSelectionRange(start, end);
39672             }else if(d.createTextRange){
39673                 var range = d.createTextRange();
39674                 range.moveStart("character", start);
39675                 range.moveEnd("character", v.length-end);
39676                 range.select();
39677             }
39678         }
39679     },
39680
39681     /**
39682      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39683      * This only takes effect if grow = true, and fires the autosize event.
39684      */
39685     autoSize : function(){
39686         if(!this.grow || !this.rendered){
39687             return;
39688         }
39689         if(!this.metrics){
39690             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39691         }
39692         var el = this.el;
39693         var v = el.dom.value;
39694         var d = document.createElement('div');
39695         d.appendChild(document.createTextNode(v));
39696         v = d.innerHTML;
39697         d = null;
39698         v += "&#160;";
39699         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39700         this.el.setWidth(w);
39701         this.fireEvent("autosize", this, w);
39702     },
39703     
39704     // private
39705     SafariOnKeyDown : function(event)
39706     {
39707         // this is a workaround for a password hang bug on chrome/ webkit.
39708         
39709         var isSelectAll = false;
39710         
39711         if(this.el.dom.selectionEnd > 0){
39712             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39713         }
39714         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39715             event.preventDefault();
39716             this.setValue('');
39717             return;
39718         }
39719         
39720         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39721             
39722             event.preventDefault();
39723             // this is very hacky as keydown always get's upper case.
39724             
39725             var cc = String.fromCharCode(event.getCharCode());
39726             
39727             
39728             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39729             
39730         }
39731         
39732         
39733     }
39734 });/*
39735  * Based on:
39736  * Ext JS Library 1.1.1
39737  * Copyright(c) 2006-2007, Ext JS, LLC.
39738  *
39739  * Originally Released Under LGPL - original licence link has changed is not relivant.
39740  *
39741  * Fork - LGPL
39742  * <script type="text/javascript">
39743  */
39744  
39745 /**
39746  * @class Roo.form.Hidden
39747  * @extends Roo.form.TextField
39748  * Simple Hidden element used on forms 
39749  * 
39750  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39751  * 
39752  * @constructor
39753  * Creates a new Hidden form element.
39754  * @param {Object} config Configuration options
39755  */
39756
39757
39758
39759 // easy hidden field...
39760 Roo.form.Hidden = function(config){
39761     Roo.form.Hidden.superclass.constructor.call(this, config);
39762 };
39763   
39764 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39765     fieldLabel:      '',
39766     inputType:      'hidden',
39767     width:          50,
39768     allowBlank:     true,
39769     labelSeparator: '',
39770     hidden:         true,
39771     itemCls :       'x-form-item-display-none'
39772
39773
39774 });
39775
39776
39777 /*
39778  * Based on:
39779  * Ext JS Library 1.1.1
39780  * Copyright(c) 2006-2007, Ext JS, LLC.
39781  *
39782  * Originally Released Under LGPL - original licence link has changed is not relivant.
39783  *
39784  * Fork - LGPL
39785  * <script type="text/javascript">
39786  */
39787  
39788 /**
39789  * @class Roo.form.TriggerField
39790  * @extends Roo.form.TextField
39791  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39792  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39793  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39794  * for which you can provide a custom implementation.  For example:
39795  * <pre><code>
39796 var trigger = new Roo.form.TriggerField();
39797 trigger.onTriggerClick = myTriggerFn;
39798 trigger.applyTo('my-field');
39799 </code></pre>
39800  *
39801  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39802  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39803  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39804  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39805  * @constructor
39806  * Create a new TriggerField.
39807  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39808  * to the base TextField)
39809  */
39810 Roo.form.TriggerField = function(config){
39811     this.mimicing = false;
39812     Roo.form.TriggerField.superclass.constructor.call(this, config);
39813 };
39814
39815 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39816     /**
39817      * @cfg {String} triggerClass A CSS class to apply to the trigger
39818      */
39819     /**
39820      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39821      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39822      */
39823     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39824     /**
39825      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39826      */
39827     hideTrigger:false,
39828
39829     /** @cfg {Boolean} grow @hide */
39830     /** @cfg {Number} growMin @hide */
39831     /** @cfg {Number} growMax @hide */
39832
39833     /**
39834      * @hide 
39835      * @method
39836      */
39837     autoSize: Roo.emptyFn,
39838     // private
39839     monitorTab : true,
39840     // private
39841     deferHeight : true,
39842
39843     
39844     actionMode : 'wrap',
39845     // private
39846     onResize : function(w, h){
39847         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39848         if(typeof w == 'number'){
39849             var x = w - this.trigger.getWidth();
39850             this.el.setWidth(this.adjustWidth('input', x));
39851             this.trigger.setStyle('left', x+'px');
39852         }
39853     },
39854
39855     // private
39856     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39857
39858     // private
39859     getResizeEl : function(){
39860         return this.wrap;
39861     },
39862
39863     // private
39864     getPositionEl : function(){
39865         return this.wrap;
39866     },
39867
39868     // private
39869     alignErrorIcon : function(){
39870         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39871     },
39872
39873     // private
39874     onRender : function(ct, position){
39875         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39876         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39877         this.trigger = this.wrap.createChild(this.triggerConfig ||
39878                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39879         if(this.hideTrigger){
39880             this.trigger.setDisplayed(false);
39881         }
39882         this.initTrigger();
39883         if(!this.width){
39884             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39885         }
39886     },
39887
39888     // private
39889     initTrigger : function(){
39890         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39891         this.trigger.addClassOnOver('x-form-trigger-over');
39892         this.trigger.addClassOnClick('x-form-trigger-click');
39893     },
39894
39895     // private
39896     onDestroy : function(){
39897         if(this.trigger){
39898             this.trigger.removeAllListeners();
39899             this.trigger.remove();
39900         }
39901         if(this.wrap){
39902             this.wrap.remove();
39903         }
39904         Roo.form.TriggerField.superclass.onDestroy.call(this);
39905     },
39906
39907     // private
39908     onFocus : function(){
39909         Roo.form.TriggerField.superclass.onFocus.call(this);
39910         if(!this.mimicing){
39911             this.wrap.addClass('x-trigger-wrap-focus');
39912             this.mimicing = true;
39913             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39914             if(this.monitorTab){
39915                 this.el.on("keydown", this.checkTab, this);
39916             }
39917         }
39918     },
39919
39920     // private
39921     checkTab : function(e){
39922         if(e.getKey() == e.TAB){
39923             this.triggerBlur();
39924         }
39925     },
39926
39927     // private
39928     onBlur : function(){
39929         // do nothing
39930     },
39931
39932     // private
39933     mimicBlur : function(e, t){
39934         if(!this.wrap.contains(t) && this.validateBlur()){
39935             this.triggerBlur();
39936         }
39937     },
39938
39939     // private
39940     triggerBlur : function(){
39941         this.mimicing = false;
39942         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39943         if(this.monitorTab){
39944             this.el.un("keydown", this.checkTab, this);
39945         }
39946         this.wrap.removeClass('x-trigger-wrap-focus');
39947         Roo.form.TriggerField.superclass.onBlur.call(this);
39948     },
39949
39950     // private
39951     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39952     validateBlur : function(e, t){
39953         return true;
39954     },
39955
39956     // private
39957     onDisable : function(){
39958         Roo.form.TriggerField.superclass.onDisable.call(this);
39959         if(this.wrap){
39960             this.wrap.addClass('x-item-disabled');
39961         }
39962     },
39963
39964     // private
39965     onEnable : function(){
39966         Roo.form.TriggerField.superclass.onEnable.call(this);
39967         if(this.wrap){
39968             this.wrap.removeClass('x-item-disabled');
39969         }
39970     },
39971
39972     // private
39973     onShow : function(){
39974         var ae = this.getActionEl();
39975         
39976         if(ae){
39977             ae.dom.style.display = '';
39978             ae.dom.style.visibility = 'visible';
39979         }
39980     },
39981
39982     // private
39983     
39984     onHide : function(){
39985         var ae = this.getActionEl();
39986         ae.dom.style.display = 'none';
39987     },
39988
39989     /**
39990      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39991      * by an implementing function.
39992      * @method
39993      * @param {EventObject} e
39994      */
39995     onTriggerClick : Roo.emptyFn
39996 });
39997
39998 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39999 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40000 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40001 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40002     initComponent : function(){
40003         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40004
40005         this.triggerConfig = {
40006             tag:'span', cls:'x-form-twin-triggers', cn:[
40007             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40008             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40009         ]};
40010     },
40011
40012     getTrigger : function(index){
40013         return this.triggers[index];
40014     },
40015
40016     initTrigger : function(){
40017         var ts = this.trigger.select('.x-form-trigger', true);
40018         this.wrap.setStyle('overflow', 'hidden');
40019         var triggerField = this;
40020         ts.each(function(t, all, index){
40021             t.hide = function(){
40022                 var w = triggerField.wrap.getWidth();
40023                 this.dom.style.display = 'none';
40024                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40025             };
40026             t.show = function(){
40027                 var w = triggerField.wrap.getWidth();
40028                 this.dom.style.display = '';
40029                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40030             };
40031             var triggerIndex = 'Trigger'+(index+1);
40032
40033             if(this['hide'+triggerIndex]){
40034                 t.dom.style.display = 'none';
40035             }
40036             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40037             t.addClassOnOver('x-form-trigger-over');
40038             t.addClassOnClick('x-form-trigger-click');
40039         }, this);
40040         this.triggers = ts.elements;
40041     },
40042
40043     onTrigger1Click : Roo.emptyFn,
40044     onTrigger2Click : Roo.emptyFn
40045 });/*
40046  * Based on:
40047  * Ext JS Library 1.1.1
40048  * Copyright(c) 2006-2007, Ext JS, LLC.
40049  *
40050  * Originally Released Under LGPL - original licence link has changed is not relivant.
40051  *
40052  * Fork - LGPL
40053  * <script type="text/javascript">
40054  */
40055  
40056 /**
40057  * @class Roo.form.TextArea
40058  * @extends Roo.form.TextField
40059  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40060  * support for auto-sizing.
40061  * @constructor
40062  * Creates a new TextArea
40063  * @param {Object} config Configuration options
40064  */
40065 Roo.form.TextArea = function(config){
40066     Roo.form.TextArea.superclass.constructor.call(this, config);
40067     // these are provided exchanges for backwards compat
40068     // minHeight/maxHeight were replaced by growMin/growMax to be
40069     // compatible with TextField growing config values
40070     if(this.minHeight !== undefined){
40071         this.growMin = this.minHeight;
40072     }
40073     if(this.maxHeight !== undefined){
40074         this.growMax = this.maxHeight;
40075     }
40076 };
40077
40078 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40079     /**
40080      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40081      */
40082     growMin : 60,
40083     /**
40084      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40085      */
40086     growMax: 1000,
40087     /**
40088      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40089      * in the field (equivalent to setting overflow: hidden, defaults to false)
40090      */
40091     preventScrollbars: false,
40092     /**
40093      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40094      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40095      */
40096
40097     // private
40098     onRender : function(ct, position){
40099         if(!this.el){
40100             this.defaultAutoCreate = {
40101                 tag: "textarea",
40102                 style:"width:300px;height:60px;",
40103                 autocomplete: "new-password"
40104             };
40105         }
40106         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40107         if(this.grow){
40108             this.textSizeEl = Roo.DomHelper.append(document.body, {
40109                 tag: "pre", cls: "x-form-grow-sizer"
40110             });
40111             if(this.preventScrollbars){
40112                 this.el.setStyle("overflow", "hidden");
40113             }
40114             this.el.setHeight(this.growMin);
40115         }
40116     },
40117
40118     onDestroy : function(){
40119         if(this.textSizeEl){
40120             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40121         }
40122         Roo.form.TextArea.superclass.onDestroy.call(this);
40123     },
40124
40125     // private
40126     onKeyUp : function(e){
40127         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40128             this.autoSize();
40129         }
40130     },
40131
40132     /**
40133      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40134      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40135      */
40136     autoSize : function(){
40137         if(!this.grow || !this.textSizeEl){
40138             return;
40139         }
40140         var el = this.el;
40141         var v = el.dom.value;
40142         var ts = this.textSizeEl;
40143
40144         ts.innerHTML = '';
40145         ts.appendChild(document.createTextNode(v));
40146         v = ts.innerHTML;
40147
40148         Roo.fly(ts).setWidth(this.el.getWidth());
40149         if(v.length < 1){
40150             v = "&#160;&#160;";
40151         }else{
40152             if(Roo.isIE){
40153                 v = v.replace(/\n/g, '<p>&#160;</p>');
40154             }
40155             v += "&#160;\n&#160;";
40156         }
40157         ts.innerHTML = v;
40158         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40159         if(h != this.lastHeight){
40160             this.lastHeight = h;
40161             this.el.setHeight(h);
40162             this.fireEvent("autosize", this, h);
40163         }
40164     }
40165 });/*
40166  * Based on:
40167  * Ext JS Library 1.1.1
40168  * Copyright(c) 2006-2007, Ext JS, LLC.
40169  *
40170  * Originally Released Under LGPL - original licence link has changed is not relivant.
40171  *
40172  * Fork - LGPL
40173  * <script type="text/javascript">
40174  */
40175  
40176
40177 /**
40178  * @class Roo.form.NumberField
40179  * @extends Roo.form.TextField
40180  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40181  * @constructor
40182  * Creates a new NumberField
40183  * @param {Object} config Configuration options
40184  */
40185 Roo.form.NumberField = function(config){
40186     Roo.form.NumberField.superclass.constructor.call(this, config);
40187 };
40188
40189 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40190     /**
40191      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40192      */
40193     fieldClass: "x-form-field x-form-num-field",
40194     /**
40195      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40196      */
40197     allowDecimals : true,
40198     /**
40199      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40200      */
40201     decimalSeparator : ".",
40202     /**
40203      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40204      */
40205     decimalPrecision : 2,
40206     /**
40207      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40208      */
40209     allowNegative : true,
40210     /**
40211      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40212      */
40213     minValue : Number.NEGATIVE_INFINITY,
40214     /**
40215      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40216      */
40217     maxValue : Number.MAX_VALUE,
40218     /**
40219      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40220      */
40221     minText : "The minimum value for this field is {0}",
40222     /**
40223      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40224      */
40225     maxText : "The maximum value for this field is {0}",
40226     /**
40227      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40228      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40229      */
40230     nanText : "{0} is not a valid number",
40231
40232     // private
40233     initEvents : function(){
40234         Roo.form.NumberField.superclass.initEvents.call(this);
40235         var allowed = "0123456789";
40236         if(this.allowDecimals){
40237             allowed += this.decimalSeparator;
40238         }
40239         if(this.allowNegative){
40240             allowed += "-";
40241         }
40242         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40243         var keyPress = function(e){
40244             var k = e.getKey();
40245             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40246                 return;
40247             }
40248             var c = e.getCharCode();
40249             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40250                 e.stopEvent();
40251             }
40252         };
40253         this.el.on("keypress", keyPress, this);
40254     },
40255
40256     // private
40257     validateValue : function(value){
40258         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40259             return false;
40260         }
40261         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40262              return true;
40263         }
40264         var num = this.parseValue(value);
40265         if(isNaN(num)){
40266             this.markInvalid(String.format(this.nanText, value));
40267             return false;
40268         }
40269         if(num < this.minValue){
40270             this.markInvalid(String.format(this.minText, this.minValue));
40271             return false;
40272         }
40273         if(num > this.maxValue){
40274             this.markInvalid(String.format(this.maxText, this.maxValue));
40275             return false;
40276         }
40277         return true;
40278     },
40279
40280     getValue : function(){
40281         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40282     },
40283
40284     // private
40285     parseValue : function(value){
40286         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40287         return isNaN(value) ? '' : value;
40288     },
40289
40290     // private
40291     fixPrecision : function(value){
40292         var nan = isNaN(value);
40293         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40294             return nan ? '' : value;
40295         }
40296         return parseFloat(value).toFixed(this.decimalPrecision);
40297     },
40298
40299     setValue : function(v){
40300         v = this.fixPrecision(v);
40301         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40302     },
40303
40304     // private
40305     decimalPrecisionFcn : function(v){
40306         return Math.floor(v);
40307     },
40308
40309     beforeBlur : function(){
40310         var v = this.parseValue(this.getRawValue());
40311         if(v){
40312             this.setValue(v);
40313         }
40314     }
40315 });/*
40316  * Based on:
40317  * Ext JS Library 1.1.1
40318  * Copyright(c) 2006-2007, Ext JS, LLC.
40319  *
40320  * Originally Released Under LGPL - original licence link has changed is not relivant.
40321  *
40322  * Fork - LGPL
40323  * <script type="text/javascript">
40324  */
40325  
40326 /**
40327  * @class Roo.form.DateField
40328  * @extends Roo.form.TriggerField
40329  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40330 * @constructor
40331 * Create a new DateField
40332 * @param {Object} config
40333  */
40334 Roo.form.DateField = function(config)
40335 {
40336     Roo.form.DateField.superclass.constructor.call(this, config);
40337     
40338       this.addEvents({
40339          
40340         /**
40341          * @event select
40342          * Fires when a date is selected
40343              * @param {Roo.form.DateField} combo This combo box
40344              * @param {Date} date The date selected
40345              */
40346         'select' : true
40347          
40348     });
40349     
40350     
40351     if(typeof this.minValue == "string") {
40352         this.minValue = this.parseDate(this.minValue);
40353     }
40354     if(typeof this.maxValue == "string") {
40355         this.maxValue = this.parseDate(this.maxValue);
40356     }
40357     this.ddMatch = null;
40358     if(this.disabledDates){
40359         var dd = this.disabledDates;
40360         var re = "(?:";
40361         for(var i = 0; i < dd.length; i++){
40362             re += dd[i];
40363             if(i != dd.length-1) {
40364                 re += "|";
40365             }
40366         }
40367         this.ddMatch = new RegExp(re + ")");
40368     }
40369 };
40370
40371 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40372     /**
40373      * @cfg {String} format
40374      * The default date format string which can be overriden for localization support.  The format must be
40375      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40376      */
40377     format : "m/d/y",
40378     /**
40379      * @cfg {String} altFormats
40380      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40381      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40382      */
40383     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40384     /**
40385      * @cfg {Array} disabledDays
40386      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40387      */
40388     disabledDays : null,
40389     /**
40390      * @cfg {String} disabledDaysText
40391      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40392      */
40393     disabledDaysText : "Disabled",
40394     /**
40395      * @cfg {Array} disabledDates
40396      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40397      * expression so they are very powerful. Some examples:
40398      * <ul>
40399      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40400      * <li>["03/08", "09/16"] would disable those days for every year</li>
40401      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40402      * <li>["03/../2006"] would disable every day in March 2006</li>
40403      * <li>["^03"] would disable every day in every March</li>
40404      * </ul>
40405      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40406      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40407      */
40408     disabledDates : null,
40409     /**
40410      * @cfg {String} disabledDatesText
40411      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40412      */
40413     disabledDatesText : "Disabled",
40414     /**
40415      * @cfg {Date/String} minValue
40416      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40417      * valid format (defaults to null).
40418      */
40419     minValue : null,
40420     /**
40421      * @cfg {Date/String} maxValue
40422      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40423      * valid format (defaults to null).
40424      */
40425     maxValue : null,
40426     /**
40427      * @cfg {String} minText
40428      * The error text to display when the date in the cell is before minValue (defaults to
40429      * 'The date in this field must be after {minValue}').
40430      */
40431     minText : "The date in this field must be equal to or after {0}",
40432     /**
40433      * @cfg {String} maxText
40434      * The error text to display when the date in the cell is after maxValue (defaults to
40435      * 'The date in this field must be before {maxValue}').
40436      */
40437     maxText : "The date in this field must be equal to or before {0}",
40438     /**
40439      * @cfg {String} invalidText
40440      * The error text to display when the date in the field is invalid (defaults to
40441      * '{value} is not a valid date - it must be in the format {format}').
40442      */
40443     invalidText : "{0} is not a valid date - it must be in the format {1}",
40444     /**
40445      * @cfg {String} triggerClass
40446      * An additional CSS class used to style the trigger button.  The trigger will always get the
40447      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40448      * which displays a calendar icon).
40449      */
40450     triggerClass : 'x-form-date-trigger',
40451     
40452
40453     /**
40454      * @cfg {Boolean} useIso
40455      * if enabled, then the date field will use a hidden field to store the 
40456      * real value as iso formated date. default (false)
40457      */ 
40458     useIso : false,
40459     /**
40460      * @cfg {String/Object} autoCreate
40461      * A DomHelper element spec, or true for a default element spec (defaults to
40462      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40463      */ 
40464     // private
40465     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40466     
40467     // private
40468     hiddenField: false,
40469     
40470     onRender : function(ct, position)
40471     {
40472         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40473         if (this.useIso) {
40474             //this.el.dom.removeAttribute('name'); 
40475             Roo.log("Changing name?");
40476             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40477             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40478                     'before', true);
40479             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40480             // prevent input submission
40481             this.hiddenName = this.name;
40482         }
40483             
40484             
40485     },
40486     
40487     // private
40488     validateValue : function(value)
40489     {
40490         value = this.formatDate(value);
40491         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40492             Roo.log('super failed');
40493             return false;
40494         }
40495         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40496              return true;
40497         }
40498         var svalue = value;
40499         value = this.parseDate(value);
40500         if(!value){
40501             Roo.log('parse date failed' + svalue);
40502             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40503             return false;
40504         }
40505         var time = value.getTime();
40506         if(this.minValue && time < this.minValue.getTime()){
40507             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40508             return false;
40509         }
40510         if(this.maxValue && time > this.maxValue.getTime()){
40511             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40512             return false;
40513         }
40514         if(this.disabledDays){
40515             var day = value.getDay();
40516             for(var i = 0; i < this.disabledDays.length; i++) {
40517                 if(day === this.disabledDays[i]){
40518                     this.markInvalid(this.disabledDaysText);
40519                     return false;
40520                 }
40521             }
40522         }
40523         var fvalue = this.formatDate(value);
40524         if(this.ddMatch && this.ddMatch.test(fvalue)){
40525             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40526             return false;
40527         }
40528         return true;
40529     },
40530
40531     // private
40532     // Provides logic to override the default TriggerField.validateBlur which just returns true
40533     validateBlur : function(){
40534         return !this.menu || !this.menu.isVisible();
40535     },
40536     
40537     getName: function()
40538     {
40539         // returns hidden if it's set..
40540         if (!this.rendered) {return ''};
40541         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40542         
40543     },
40544
40545     /**
40546      * Returns the current date value of the date field.
40547      * @return {Date} The date value
40548      */
40549     getValue : function(){
40550         
40551         return  this.hiddenField ?
40552                 this.hiddenField.value :
40553                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40554     },
40555
40556     /**
40557      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40558      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40559      * (the default format used is "m/d/y").
40560      * <br />Usage:
40561      * <pre><code>
40562 //All of these calls set the same date value (May 4, 2006)
40563
40564 //Pass a date object:
40565 var dt = new Date('5/4/06');
40566 dateField.setValue(dt);
40567
40568 //Pass a date string (default format):
40569 dateField.setValue('5/4/06');
40570
40571 //Pass a date string (custom format):
40572 dateField.format = 'Y-m-d';
40573 dateField.setValue('2006-5-4');
40574 </code></pre>
40575      * @param {String/Date} date The date or valid date string
40576      */
40577     setValue : function(date){
40578         if (this.hiddenField) {
40579             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40580         }
40581         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40582         // make sure the value field is always stored as a date..
40583         this.value = this.parseDate(date);
40584         
40585         
40586     },
40587
40588     // private
40589     parseDate : function(value){
40590         if(!value || value instanceof Date){
40591             return value;
40592         }
40593         var v = Date.parseDate(value, this.format);
40594          if (!v && this.useIso) {
40595             v = Date.parseDate(value, 'Y-m-d');
40596         }
40597         if(!v && this.altFormats){
40598             if(!this.altFormatsArray){
40599                 this.altFormatsArray = this.altFormats.split("|");
40600             }
40601             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40602                 v = Date.parseDate(value, this.altFormatsArray[i]);
40603             }
40604         }
40605         return v;
40606     },
40607
40608     // private
40609     formatDate : function(date, fmt){
40610         return (!date || !(date instanceof Date)) ?
40611                date : date.dateFormat(fmt || this.format);
40612     },
40613
40614     // private
40615     menuListeners : {
40616         select: function(m, d){
40617             
40618             this.setValue(d);
40619             this.fireEvent('select', this, d);
40620         },
40621         show : function(){ // retain focus styling
40622             this.onFocus();
40623         },
40624         hide : function(){
40625             this.focus.defer(10, this);
40626             var ml = this.menuListeners;
40627             this.menu.un("select", ml.select,  this);
40628             this.menu.un("show", ml.show,  this);
40629             this.menu.un("hide", ml.hide,  this);
40630         }
40631     },
40632
40633     // private
40634     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40635     onTriggerClick : function(){
40636         if(this.disabled){
40637             return;
40638         }
40639         if(this.menu == null){
40640             this.menu = new Roo.menu.DateMenu();
40641         }
40642         Roo.apply(this.menu.picker,  {
40643             showClear: this.allowBlank,
40644             minDate : this.minValue,
40645             maxDate : this.maxValue,
40646             disabledDatesRE : this.ddMatch,
40647             disabledDatesText : this.disabledDatesText,
40648             disabledDays : this.disabledDays,
40649             disabledDaysText : this.disabledDaysText,
40650             format : this.useIso ? 'Y-m-d' : this.format,
40651             minText : String.format(this.minText, this.formatDate(this.minValue)),
40652             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40653         });
40654         this.menu.on(Roo.apply({}, this.menuListeners, {
40655             scope:this
40656         }));
40657         this.menu.picker.setValue(this.getValue() || new Date());
40658         this.menu.show(this.el, "tl-bl?");
40659     },
40660
40661     beforeBlur : function(){
40662         var v = this.parseDate(this.getRawValue());
40663         if(v){
40664             this.setValue(v);
40665         }
40666     },
40667
40668     /*@
40669      * overide
40670      * 
40671      */
40672     isDirty : function() {
40673         if(this.disabled) {
40674             return false;
40675         }
40676         
40677         if(typeof(this.startValue) === 'undefined'){
40678             return false;
40679         }
40680         
40681         return String(this.getValue()) !== String(this.startValue);
40682         
40683     },
40684     // @overide
40685     cleanLeadingSpace : function(e)
40686     {
40687        return;
40688     }
40689     
40690 });/*
40691  * Based on:
40692  * Ext JS Library 1.1.1
40693  * Copyright(c) 2006-2007, Ext JS, LLC.
40694  *
40695  * Originally Released Under LGPL - original licence link has changed is not relivant.
40696  *
40697  * Fork - LGPL
40698  * <script type="text/javascript">
40699  */
40700  
40701 /**
40702  * @class Roo.form.MonthField
40703  * @extends Roo.form.TriggerField
40704  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40705 * @constructor
40706 * Create a new MonthField
40707 * @param {Object} config
40708  */
40709 Roo.form.MonthField = function(config){
40710     
40711     Roo.form.MonthField.superclass.constructor.call(this, config);
40712     
40713       this.addEvents({
40714          
40715         /**
40716          * @event select
40717          * Fires when a date is selected
40718              * @param {Roo.form.MonthFieeld} combo This combo box
40719              * @param {Date} date The date selected
40720              */
40721         'select' : true
40722          
40723     });
40724     
40725     
40726     if(typeof this.minValue == "string") {
40727         this.minValue = this.parseDate(this.minValue);
40728     }
40729     if(typeof this.maxValue == "string") {
40730         this.maxValue = this.parseDate(this.maxValue);
40731     }
40732     this.ddMatch = null;
40733     if(this.disabledDates){
40734         var dd = this.disabledDates;
40735         var re = "(?:";
40736         for(var i = 0; i < dd.length; i++){
40737             re += dd[i];
40738             if(i != dd.length-1) {
40739                 re += "|";
40740             }
40741         }
40742         this.ddMatch = new RegExp(re + ")");
40743     }
40744 };
40745
40746 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40747     /**
40748      * @cfg {String} format
40749      * The default date format string which can be overriden for localization support.  The format must be
40750      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40751      */
40752     format : "M Y",
40753     /**
40754      * @cfg {String} altFormats
40755      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40756      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40757      */
40758     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40759     /**
40760      * @cfg {Array} disabledDays
40761      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40762      */
40763     disabledDays : [0,1,2,3,4,5,6],
40764     /**
40765      * @cfg {String} disabledDaysText
40766      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40767      */
40768     disabledDaysText : "Disabled",
40769     /**
40770      * @cfg {Array} disabledDates
40771      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40772      * expression so they are very powerful. Some examples:
40773      * <ul>
40774      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40775      * <li>["03/08", "09/16"] would disable those days for every year</li>
40776      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40777      * <li>["03/../2006"] would disable every day in March 2006</li>
40778      * <li>["^03"] would disable every day in every March</li>
40779      * </ul>
40780      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40781      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40782      */
40783     disabledDates : null,
40784     /**
40785      * @cfg {String} disabledDatesText
40786      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40787      */
40788     disabledDatesText : "Disabled",
40789     /**
40790      * @cfg {Date/String} minValue
40791      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40792      * valid format (defaults to null).
40793      */
40794     minValue : null,
40795     /**
40796      * @cfg {Date/String} maxValue
40797      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40798      * valid format (defaults to null).
40799      */
40800     maxValue : null,
40801     /**
40802      * @cfg {String} minText
40803      * The error text to display when the date in the cell is before minValue (defaults to
40804      * 'The date in this field must be after {minValue}').
40805      */
40806     minText : "The date in this field must be equal to or after {0}",
40807     /**
40808      * @cfg {String} maxTextf
40809      * The error text to display when the date in the cell is after maxValue (defaults to
40810      * 'The date in this field must be before {maxValue}').
40811      */
40812     maxText : "The date in this field must be equal to or before {0}",
40813     /**
40814      * @cfg {String} invalidText
40815      * The error text to display when the date in the field is invalid (defaults to
40816      * '{value} is not a valid date - it must be in the format {format}').
40817      */
40818     invalidText : "{0} is not a valid date - it must be in the format {1}",
40819     /**
40820      * @cfg {String} triggerClass
40821      * An additional CSS class used to style the trigger button.  The trigger will always get the
40822      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40823      * which displays a calendar icon).
40824      */
40825     triggerClass : 'x-form-date-trigger',
40826     
40827
40828     /**
40829      * @cfg {Boolean} useIso
40830      * if enabled, then the date field will use a hidden field to store the 
40831      * real value as iso formated date. default (true)
40832      */ 
40833     useIso : true,
40834     /**
40835      * @cfg {String/Object} autoCreate
40836      * A DomHelper element spec, or true for a default element spec (defaults to
40837      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40838      */ 
40839     // private
40840     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40841     
40842     // private
40843     hiddenField: false,
40844     
40845     hideMonthPicker : false,
40846     
40847     onRender : function(ct, position)
40848     {
40849         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40850         if (this.useIso) {
40851             this.el.dom.removeAttribute('name'); 
40852             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40853                     'before', true);
40854             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40855             // prevent input submission
40856             this.hiddenName = this.name;
40857         }
40858             
40859             
40860     },
40861     
40862     // private
40863     validateValue : function(value)
40864     {
40865         value = this.formatDate(value);
40866         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40867             return false;
40868         }
40869         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40870              return true;
40871         }
40872         var svalue = value;
40873         value = this.parseDate(value);
40874         if(!value){
40875             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40876             return false;
40877         }
40878         var time = value.getTime();
40879         if(this.minValue && time < this.minValue.getTime()){
40880             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40881             return false;
40882         }
40883         if(this.maxValue && time > this.maxValue.getTime()){
40884             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40885             return false;
40886         }
40887         /*if(this.disabledDays){
40888             var day = value.getDay();
40889             for(var i = 0; i < this.disabledDays.length; i++) {
40890                 if(day === this.disabledDays[i]){
40891                     this.markInvalid(this.disabledDaysText);
40892                     return false;
40893                 }
40894             }
40895         }
40896         */
40897         var fvalue = this.formatDate(value);
40898         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40899             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40900             return false;
40901         }
40902         */
40903         return true;
40904     },
40905
40906     // private
40907     // Provides logic to override the default TriggerField.validateBlur which just returns true
40908     validateBlur : function(){
40909         return !this.menu || !this.menu.isVisible();
40910     },
40911
40912     /**
40913      * Returns the current date value of the date field.
40914      * @return {Date} The date value
40915      */
40916     getValue : function(){
40917         
40918         
40919         
40920         return  this.hiddenField ?
40921                 this.hiddenField.value :
40922                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40923     },
40924
40925     /**
40926      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40927      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40928      * (the default format used is "m/d/y").
40929      * <br />Usage:
40930      * <pre><code>
40931 //All of these calls set the same date value (May 4, 2006)
40932
40933 //Pass a date object:
40934 var dt = new Date('5/4/06');
40935 monthField.setValue(dt);
40936
40937 //Pass a date string (default format):
40938 monthField.setValue('5/4/06');
40939
40940 //Pass a date string (custom format):
40941 monthField.format = 'Y-m-d';
40942 monthField.setValue('2006-5-4');
40943 </code></pre>
40944      * @param {String/Date} date The date or valid date string
40945      */
40946     setValue : function(date){
40947         Roo.log('month setValue' + date);
40948         // can only be first of month..
40949         
40950         var val = this.parseDate(date);
40951         
40952         if (this.hiddenField) {
40953             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40954         }
40955         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40956         this.value = this.parseDate(date);
40957     },
40958
40959     // private
40960     parseDate : function(value){
40961         if(!value || value instanceof Date){
40962             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40963             return value;
40964         }
40965         var v = Date.parseDate(value, this.format);
40966         if (!v && this.useIso) {
40967             v = Date.parseDate(value, 'Y-m-d');
40968         }
40969         if (v) {
40970             // 
40971             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40972         }
40973         
40974         
40975         if(!v && this.altFormats){
40976             if(!this.altFormatsArray){
40977                 this.altFormatsArray = this.altFormats.split("|");
40978             }
40979             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40980                 v = Date.parseDate(value, this.altFormatsArray[i]);
40981             }
40982         }
40983         return v;
40984     },
40985
40986     // private
40987     formatDate : function(date, fmt){
40988         return (!date || !(date instanceof Date)) ?
40989                date : date.dateFormat(fmt || this.format);
40990     },
40991
40992     // private
40993     menuListeners : {
40994         select: function(m, d){
40995             this.setValue(d);
40996             this.fireEvent('select', this, d);
40997         },
40998         show : function(){ // retain focus styling
40999             this.onFocus();
41000         },
41001         hide : function(){
41002             this.focus.defer(10, this);
41003             var ml = this.menuListeners;
41004             this.menu.un("select", ml.select,  this);
41005             this.menu.un("show", ml.show,  this);
41006             this.menu.un("hide", ml.hide,  this);
41007         }
41008     },
41009     // private
41010     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41011     onTriggerClick : function(){
41012         if(this.disabled){
41013             return;
41014         }
41015         if(this.menu == null){
41016             this.menu = new Roo.menu.DateMenu();
41017            
41018         }
41019         
41020         Roo.apply(this.menu.picker,  {
41021             
41022             showClear: this.allowBlank,
41023             minDate : this.minValue,
41024             maxDate : this.maxValue,
41025             disabledDatesRE : this.ddMatch,
41026             disabledDatesText : this.disabledDatesText,
41027             
41028             format : this.useIso ? 'Y-m-d' : this.format,
41029             minText : String.format(this.minText, this.formatDate(this.minValue)),
41030             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41031             
41032         });
41033          this.menu.on(Roo.apply({}, this.menuListeners, {
41034             scope:this
41035         }));
41036        
41037         
41038         var m = this.menu;
41039         var p = m.picker;
41040         
41041         // hide month picker get's called when we called by 'before hide';
41042         
41043         var ignorehide = true;
41044         p.hideMonthPicker  = function(disableAnim){
41045             if (ignorehide) {
41046                 return;
41047             }
41048              if(this.monthPicker){
41049                 Roo.log("hideMonthPicker called");
41050                 if(disableAnim === true){
41051                     this.monthPicker.hide();
41052                 }else{
41053                     this.monthPicker.slideOut('t', {duration:.2});
41054                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41055                     p.fireEvent("select", this, this.value);
41056                     m.hide();
41057                 }
41058             }
41059         }
41060         
41061         Roo.log('picker set value');
41062         Roo.log(this.getValue());
41063         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41064         m.show(this.el, 'tl-bl?');
41065         ignorehide  = false;
41066         // this will trigger hideMonthPicker..
41067         
41068         
41069         // hidden the day picker
41070         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41071         
41072         
41073         
41074       
41075         
41076         p.showMonthPicker.defer(100, p);
41077     
41078         
41079        
41080     },
41081
41082     beforeBlur : function(){
41083         var v = this.parseDate(this.getRawValue());
41084         if(v){
41085             this.setValue(v);
41086         }
41087     }
41088
41089     /** @cfg {Boolean} grow @hide */
41090     /** @cfg {Number} growMin @hide */
41091     /** @cfg {Number} growMax @hide */
41092     /**
41093      * @hide
41094      * @method autoSize
41095      */
41096 });/*
41097  * Based on:
41098  * Ext JS Library 1.1.1
41099  * Copyright(c) 2006-2007, Ext JS, LLC.
41100  *
41101  * Originally Released Under LGPL - original licence link has changed is not relivant.
41102  *
41103  * Fork - LGPL
41104  * <script type="text/javascript">
41105  */
41106  
41107
41108 /**
41109  * @class Roo.form.ComboBox
41110  * @extends Roo.form.TriggerField
41111  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41112  * @constructor
41113  * Create a new ComboBox.
41114  * @param {Object} config Configuration options
41115  */
41116 Roo.form.ComboBox = function(config){
41117     Roo.form.ComboBox.superclass.constructor.call(this, config);
41118     this.addEvents({
41119         /**
41120          * @event expand
41121          * Fires when the dropdown list is expanded
41122              * @param {Roo.form.ComboBox} combo This combo box
41123              */
41124         'expand' : true,
41125         /**
41126          * @event collapse
41127          * Fires when the dropdown list is collapsed
41128              * @param {Roo.form.ComboBox} combo This combo box
41129              */
41130         'collapse' : true,
41131         /**
41132          * @event beforeselect
41133          * Fires before a list item is selected. Return false to cancel the selection.
41134              * @param {Roo.form.ComboBox} combo This combo box
41135              * @param {Roo.data.Record} record The data record returned from the underlying store
41136              * @param {Number} index The index of the selected item in the dropdown list
41137              */
41138         'beforeselect' : true,
41139         /**
41140          * @event select
41141          * Fires when a list item is selected
41142              * @param {Roo.form.ComboBox} combo This combo box
41143              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41144              * @param {Number} index The index of the selected item in the dropdown list
41145              */
41146         'select' : true,
41147         /**
41148          * @event beforequery
41149          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41150          * The event object passed has these properties:
41151              * @param {Roo.form.ComboBox} combo This combo box
41152              * @param {String} query The query
41153              * @param {Boolean} forceAll true to force "all" query
41154              * @param {Boolean} cancel true to cancel the query
41155              * @param {Object} e The query event object
41156              */
41157         'beforequery': true,
41158          /**
41159          * @event add
41160          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41161              * @param {Roo.form.ComboBox} combo This combo box
41162              */
41163         'add' : true,
41164         /**
41165          * @event edit
41166          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41167              * @param {Roo.form.ComboBox} combo This combo box
41168              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41169              */
41170         'edit' : true
41171         
41172         
41173     });
41174     if(this.transform){
41175         this.allowDomMove = false;
41176         var s = Roo.getDom(this.transform);
41177         if(!this.hiddenName){
41178             this.hiddenName = s.name;
41179         }
41180         if(!this.store){
41181             this.mode = 'local';
41182             var d = [], opts = s.options;
41183             for(var i = 0, len = opts.length;i < len; i++){
41184                 var o = opts[i];
41185                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41186                 if(o.selected) {
41187                     this.value = value;
41188                 }
41189                 d.push([value, o.text]);
41190             }
41191             this.store = new Roo.data.SimpleStore({
41192                 'id': 0,
41193                 fields: ['value', 'text'],
41194                 data : d
41195             });
41196             this.valueField = 'value';
41197             this.displayField = 'text';
41198         }
41199         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41200         if(!this.lazyRender){
41201             this.target = true;
41202             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41203             s.parentNode.removeChild(s); // remove it
41204             this.render(this.el.parentNode);
41205         }else{
41206             s.parentNode.removeChild(s); // remove it
41207         }
41208
41209     }
41210     if (this.store) {
41211         this.store = Roo.factory(this.store, Roo.data);
41212     }
41213     
41214     this.selectedIndex = -1;
41215     if(this.mode == 'local'){
41216         if(config.queryDelay === undefined){
41217             this.queryDelay = 10;
41218         }
41219         if(config.minChars === undefined){
41220             this.minChars = 0;
41221         }
41222     }
41223 };
41224
41225 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41226     /**
41227      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41228      */
41229     /**
41230      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41231      * rendering into an Roo.Editor, defaults to false)
41232      */
41233     /**
41234      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41235      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41236      */
41237     /**
41238      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41239      */
41240     /**
41241      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41242      * the dropdown list (defaults to undefined, with no header element)
41243      */
41244
41245      /**
41246      * @cfg {String/Roo.Template} tpl The template to use to render the output
41247      */
41248      
41249     // private
41250     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41251     /**
41252      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41253      */
41254     listWidth: undefined,
41255     /**
41256      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41257      * mode = 'remote' or 'text' if mode = 'local')
41258      */
41259     displayField: undefined,
41260     /**
41261      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41262      * mode = 'remote' or 'value' if mode = 'local'). 
41263      * Note: use of a valueField requires the user make a selection
41264      * in order for a value to be mapped.
41265      */
41266     valueField: undefined,
41267     
41268     
41269     /**
41270      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41271      * field's data value (defaults to the underlying DOM element's name)
41272      */
41273     hiddenName: undefined,
41274     /**
41275      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41276      */
41277     listClass: '',
41278     /**
41279      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41280      */
41281     selectedClass: 'x-combo-selected',
41282     /**
41283      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41284      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41285      * which displays a downward arrow icon).
41286      */
41287     triggerClass : 'x-form-arrow-trigger',
41288     /**
41289      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41290      */
41291     shadow:'sides',
41292     /**
41293      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41294      * anchor positions (defaults to 'tl-bl')
41295      */
41296     listAlign: 'tl-bl?',
41297     /**
41298      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41299      */
41300     maxHeight: 300,
41301     /**
41302      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41303      * query specified by the allQuery config option (defaults to 'query')
41304      */
41305     triggerAction: 'query',
41306     /**
41307      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41308      * (defaults to 4, does not apply if editable = false)
41309      */
41310     minChars : 4,
41311     /**
41312      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41313      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41314      */
41315     typeAhead: false,
41316     /**
41317      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41318      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41319      */
41320     queryDelay: 500,
41321     /**
41322      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41323      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41324      */
41325     pageSize: 0,
41326     /**
41327      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41328      * when editable = true (defaults to false)
41329      */
41330     selectOnFocus:false,
41331     /**
41332      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41333      */
41334     queryParam: 'query',
41335     /**
41336      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41337      * when mode = 'remote' (defaults to 'Loading...')
41338      */
41339     loadingText: 'Loading...',
41340     /**
41341      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41342      */
41343     resizable: false,
41344     /**
41345      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41346      */
41347     handleHeight : 8,
41348     /**
41349      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41350      * traditional select (defaults to true)
41351      */
41352     editable: true,
41353     /**
41354      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41355      */
41356     allQuery: '',
41357     /**
41358      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41359      */
41360     mode: 'remote',
41361     /**
41362      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41363      * listWidth has a higher value)
41364      */
41365     minListWidth : 70,
41366     /**
41367      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41368      * allow the user to set arbitrary text into the field (defaults to false)
41369      */
41370     forceSelection:false,
41371     /**
41372      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41373      * if typeAhead = true (defaults to 250)
41374      */
41375     typeAheadDelay : 250,
41376     /**
41377      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41378      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41379      */
41380     valueNotFoundText : undefined,
41381     /**
41382      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41383      */
41384     blockFocus : false,
41385     
41386     /**
41387      * @cfg {Boolean} disableClear Disable showing of clear button.
41388      */
41389     disableClear : false,
41390     /**
41391      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41392      */
41393     alwaysQuery : false,
41394     
41395     //private
41396     addicon : false,
41397     editicon: false,
41398     
41399     // element that contains real text value.. (when hidden is used..)
41400      
41401     // private
41402     onRender : function(ct, position)
41403     {
41404         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41405         
41406         if(this.hiddenName){
41407             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41408                     'before', true);
41409             this.hiddenField.value =
41410                 this.hiddenValue !== undefined ? this.hiddenValue :
41411                 this.value !== undefined ? this.value : '';
41412
41413             // prevent input submission
41414             this.el.dom.removeAttribute('name');
41415              
41416              
41417         }
41418         
41419         if(Roo.isGecko){
41420             this.el.dom.setAttribute('autocomplete', 'off');
41421         }
41422
41423         var cls = 'x-combo-list';
41424
41425         this.list = new Roo.Layer({
41426             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41427         });
41428
41429         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41430         this.list.setWidth(lw);
41431         this.list.swallowEvent('mousewheel');
41432         this.assetHeight = 0;
41433
41434         if(this.title){
41435             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41436             this.assetHeight += this.header.getHeight();
41437         }
41438
41439         this.innerList = this.list.createChild({cls:cls+'-inner'});
41440         this.innerList.on('mouseover', this.onViewOver, this);
41441         this.innerList.on('mousemove', this.onViewMove, this);
41442         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41443         
41444         if(this.allowBlank && !this.pageSize && !this.disableClear){
41445             this.footer = this.list.createChild({cls:cls+'-ft'});
41446             this.pageTb = new Roo.Toolbar(this.footer);
41447            
41448         }
41449         if(this.pageSize){
41450             this.footer = this.list.createChild({cls:cls+'-ft'});
41451             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41452                     {pageSize: this.pageSize});
41453             
41454         }
41455         
41456         if (this.pageTb && this.allowBlank && !this.disableClear) {
41457             var _this = this;
41458             this.pageTb.add(new Roo.Toolbar.Fill(), {
41459                 cls: 'x-btn-icon x-btn-clear',
41460                 text: '&#160;',
41461                 handler: function()
41462                 {
41463                     _this.collapse();
41464                     _this.clearValue();
41465                     _this.onSelect(false, -1);
41466                 }
41467             });
41468         }
41469         if (this.footer) {
41470             this.assetHeight += this.footer.getHeight();
41471         }
41472         
41473
41474         if(!this.tpl){
41475             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41476         }
41477
41478         this.view = new Roo.View(this.innerList, this.tpl, {
41479             singleSelect:true,
41480             store: this.store,
41481             selectedClass: this.selectedClass
41482         });
41483
41484         this.view.on('click', this.onViewClick, this);
41485
41486         this.store.on('beforeload', this.onBeforeLoad, this);
41487         this.store.on('load', this.onLoad, this);
41488         this.store.on('loadexception', this.onLoadException, this);
41489
41490         if(this.resizable){
41491             this.resizer = new Roo.Resizable(this.list,  {
41492                pinned:true, handles:'se'
41493             });
41494             this.resizer.on('resize', function(r, w, h){
41495                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41496                 this.listWidth = w;
41497                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41498                 this.restrictHeight();
41499             }, this);
41500             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41501         }
41502         if(!this.editable){
41503             this.editable = true;
41504             this.setEditable(false);
41505         }  
41506         
41507         
41508         if (typeof(this.events.add.listeners) != 'undefined') {
41509             
41510             this.addicon = this.wrap.createChild(
41511                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41512        
41513             this.addicon.on('click', function(e) {
41514                 this.fireEvent('add', this);
41515             }, this);
41516         }
41517         if (typeof(this.events.edit.listeners) != 'undefined') {
41518             
41519             this.editicon = this.wrap.createChild(
41520                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41521             if (this.addicon) {
41522                 this.editicon.setStyle('margin-left', '40px');
41523             }
41524             this.editicon.on('click', function(e) {
41525                 
41526                 // we fire even  if inothing is selected..
41527                 this.fireEvent('edit', this, this.lastData );
41528                 
41529             }, this);
41530         }
41531         
41532         
41533         
41534     },
41535
41536     // private
41537     initEvents : function(){
41538         Roo.form.ComboBox.superclass.initEvents.call(this);
41539
41540         this.keyNav = new Roo.KeyNav(this.el, {
41541             "up" : function(e){
41542                 this.inKeyMode = true;
41543                 this.selectPrev();
41544             },
41545
41546             "down" : function(e){
41547                 if(!this.isExpanded()){
41548                     this.onTriggerClick();
41549                 }else{
41550                     this.inKeyMode = true;
41551                     this.selectNext();
41552                 }
41553             },
41554
41555             "enter" : function(e){
41556                 this.onViewClick();
41557                 //return true;
41558             },
41559
41560             "esc" : function(e){
41561                 this.collapse();
41562             },
41563
41564             "tab" : function(e){
41565                 this.onViewClick(false);
41566                 this.fireEvent("specialkey", this, e);
41567                 return true;
41568             },
41569
41570             scope : this,
41571
41572             doRelay : function(foo, bar, hname){
41573                 if(hname == 'down' || this.scope.isExpanded()){
41574                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41575                 }
41576                 return true;
41577             },
41578
41579             forceKeyDown: true
41580         });
41581         this.queryDelay = Math.max(this.queryDelay || 10,
41582                 this.mode == 'local' ? 10 : 250);
41583         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41584         if(this.typeAhead){
41585             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41586         }
41587         if(this.editable !== false){
41588             this.el.on("keyup", this.onKeyUp, this);
41589         }
41590         if(this.forceSelection){
41591             this.on('blur', this.doForce, this);
41592         }
41593     },
41594
41595     onDestroy : function(){
41596         if(this.view){
41597             this.view.setStore(null);
41598             this.view.el.removeAllListeners();
41599             this.view.el.remove();
41600             this.view.purgeListeners();
41601         }
41602         if(this.list){
41603             this.list.destroy();
41604         }
41605         if(this.store){
41606             this.store.un('beforeload', this.onBeforeLoad, this);
41607             this.store.un('load', this.onLoad, this);
41608             this.store.un('loadexception', this.onLoadException, this);
41609         }
41610         Roo.form.ComboBox.superclass.onDestroy.call(this);
41611     },
41612
41613     // private
41614     fireKey : function(e){
41615         if(e.isNavKeyPress() && !this.list.isVisible()){
41616             this.fireEvent("specialkey", this, e);
41617         }
41618     },
41619
41620     // private
41621     onResize: function(w, h){
41622         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41623         
41624         if(typeof w != 'number'){
41625             // we do not handle it!?!?
41626             return;
41627         }
41628         var tw = this.trigger.getWidth();
41629         tw += this.addicon ? this.addicon.getWidth() : 0;
41630         tw += this.editicon ? this.editicon.getWidth() : 0;
41631         var x = w - tw;
41632         this.el.setWidth( this.adjustWidth('input', x));
41633             
41634         this.trigger.setStyle('left', x+'px');
41635         
41636         if(this.list && this.listWidth === undefined){
41637             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41638             this.list.setWidth(lw);
41639             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41640         }
41641         
41642     
41643         
41644     },
41645
41646     /**
41647      * Allow or prevent the user from directly editing the field text.  If false is passed,
41648      * the user will only be able to select from the items defined in the dropdown list.  This method
41649      * is the runtime equivalent of setting the 'editable' config option at config time.
41650      * @param {Boolean} value True to allow the user to directly edit the field text
41651      */
41652     setEditable : function(value){
41653         if(value == this.editable){
41654             return;
41655         }
41656         this.editable = value;
41657         if(!value){
41658             this.el.dom.setAttribute('readOnly', true);
41659             this.el.on('mousedown', this.onTriggerClick,  this);
41660             this.el.addClass('x-combo-noedit');
41661         }else{
41662             this.el.dom.setAttribute('readOnly', false);
41663             this.el.un('mousedown', this.onTriggerClick,  this);
41664             this.el.removeClass('x-combo-noedit');
41665         }
41666     },
41667
41668     // private
41669     onBeforeLoad : function(){
41670         if(!this.hasFocus){
41671             return;
41672         }
41673         this.innerList.update(this.loadingText ?
41674                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41675         this.restrictHeight();
41676         this.selectedIndex = -1;
41677     },
41678
41679     // private
41680     onLoad : function(){
41681         if(!this.hasFocus){
41682             return;
41683         }
41684         if(this.store.getCount() > 0){
41685             this.expand();
41686             this.restrictHeight();
41687             if(this.lastQuery == this.allQuery){
41688                 if(this.editable){
41689                     this.el.dom.select();
41690                 }
41691                 if(!this.selectByValue(this.value, true)){
41692                     this.select(0, true);
41693                 }
41694             }else{
41695                 this.selectNext();
41696                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41697                     this.taTask.delay(this.typeAheadDelay);
41698                 }
41699             }
41700         }else{
41701             this.onEmptyResults();
41702         }
41703         //this.el.focus();
41704     },
41705     // private
41706     onLoadException : function()
41707     {
41708         this.collapse();
41709         Roo.log(this.store.reader.jsonData);
41710         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41711             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41712         }
41713         
41714         
41715     },
41716     // private
41717     onTypeAhead : function(){
41718         if(this.store.getCount() > 0){
41719             var r = this.store.getAt(0);
41720             var newValue = r.data[this.displayField];
41721             var len = newValue.length;
41722             var selStart = this.getRawValue().length;
41723             if(selStart != len){
41724                 this.setRawValue(newValue);
41725                 this.selectText(selStart, newValue.length);
41726             }
41727         }
41728     },
41729
41730     // private
41731     onSelect : function(record, index){
41732         if(this.fireEvent('beforeselect', this, record, index) !== false){
41733             this.setFromData(index > -1 ? record.data : false);
41734             this.collapse();
41735             this.fireEvent('select', this, record, index);
41736         }
41737     },
41738
41739     /**
41740      * Returns the currently selected field value or empty string if no value is set.
41741      * @return {String} value The selected value
41742      */
41743     getValue : function(){
41744         if(this.valueField){
41745             return typeof this.value != 'undefined' ? this.value : '';
41746         }
41747         return Roo.form.ComboBox.superclass.getValue.call(this);
41748     },
41749
41750     /**
41751      * Clears any text/value currently set in the field
41752      */
41753     clearValue : function(){
41754         if(this.hiddenField){
41755             this.hiddenField.value = '';
41756         }
41757         this.value = '';
41758         this.setRawValue('');
41759         this.lastSelectionText = '';
41760         
41761     },
41762
41763     /**
41764      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41765      * will be displayed in the field.  If the value does not match the data value of an existing item,
41766      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41767      * Otherwise the field will be blank (although the value will still be set).
41768      * @param {String} value The value to match
41769      */
41770     setValue : function(v){
41771         var text = v;
41772         if(this.valueField){
41773             var r = this.findRecord(this.valueField, v);
41774             if(r){
41775                 text = r.data[this.displayField];
41776             }else if(this.valueNotFoundText !== undefined){
41777                 text = this.valueNotFoundText;
41778             }
41779         }
41780         this.lastSelectionText = text;
41781         if(this.hiddenField){
41782             this.hiddenField.value = v;
41783         }
41784         Roo.form.ComboBox.superclass.setValue.call(this, text);
41785         this.value = v;
41786     },
41787     /**
41788      * @property {Object} the last set data for the element
41789      */
41790     
41791     lastData : false,
41792     /**
41793      * Sets the value of the field based on a object which is related to the record format for the store.
41794      * @param {Object} value the value to set as. or false on reset?
41795      */
41796     setFromData : function(o){
41797         var dv = ''; // display value
41798         var vv = ''; // value value..
41799         this.lastData = o;
41800         if (this.displayField) {
41801             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41802         } else {
41803             // this is an error condition!!!
41804             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41805         }
41806         
41807         if(this.valueField){
41808             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41809         }
41810         if(this.hiddenField){
41811             this.hiddenField.value = vv;
41812             
41813             this.lastSelectionText = dv;
41814             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41815             this.value = vv;
41816             return;
41817         }
41818         // no hidden field.. - we store the value in 'value', but still display
41819         // display field!!!!
41820         this.lastSelectionText = dv;
41821         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41822         this.value = vv;
41823         
41824         
41825     },
41826     // private
41827     reset : function(){
41828         // overridden so that last data is reset..
41829         this.setValue(this.resetValue);
41830         this.originalValue = this.getValue();
41831         this.clearInvalid();
41832         this.lastData = false;
41833         if (this.view) {
41834             this.view.clearSelections();
41835         }
41836     },
41837     // private
41838     findRecord : function(prop, value){
41839         var record;
41840         if(this.store.getCount() > 0){
41841             this.store.each(function(r){
41842                 if(r.data[prop] == value){
41843                     record = r;
41844                     return false;
41845                 }
41846                 return true;
41847             });
41848         }
41849         return record;
41850     },
41851     
41852     getName: function()
41853     {
41854         // returns hidden if it's set..
41855         if (!this.rendered) {return ''};
41856         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41857         
41858     },
41859     // private
41860     onViewMove : function(e, t){
41861         this.inKeyMode = false;
41862     },
41863
41864     // private
41865     onViewOver : function(e, t){
41866         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41867             return;
41868         }
41869         var item = this.view.findItemFromChild(t);
41870         if(item){
41871             var index = this.view.indexOf(item);
41872             this.select(index, false);
41873         }
41874     },
41875
41876     // private
41877     onViewClick : function(doFocus)
41878     {
41879         var index = this.view.getSelectedIndexes()[0];
41880         var r = this.store.getAt(index);
41881         if(r){
41882             this.onSelect(r, index);
41883         }
41884         if(doFocus !== false && !this.blockFocus){
41885             this.el.focus();
41886         }
41887     },
41888
41889     // private
41890     restrictHeight : function(){
41891         this.innerList.dom.style.height = '';
41892         var inner = this.innerList.dom;
41893         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41894         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41895         this.list.beginUpdate();
41896         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41897         this.list.alignTo(this.el, this.listAlign);
41898         this.list.endUpdate();
41899     },
41900
41901     // private
41902     onEmptyResults : function(){
41903         this.collapse();
41904     },
41905
41906     /**
41907      * Returns true if the dropdown list is expanded, else false.
41908      */
41909     isExpanded : function(){
41910         return this.list.isVisible();
41911     },
41912
41913     /**
41914      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41915      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41916      * @param {String} value The data value of the item to select
41917      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41918      * selected item if it is not currently in view (defaults to true)
41919      * @return {Boolean} True if the value matched an item in the list, else false
41920      */
41921     selectByValue : function(v, scrollIntoView){
41922         if(v !== undefined && v !== null){
41923             var r = this.findRecord(this.valueField || this.displayField, v);
41924             if(r){
41925                 this.select(this.store.indexOf(r), scrollIntoView);
41926                 return true;
41927             }
41928         }
41929         return false;
41930     },
41931
41932     /**
41933      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41934      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41935      * @param {Number} index The zero-based index of the list item to select
41936      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41937      * selected item if it is not currently in view (defaults to true)
41938      */
41939     select : function(index, scrollIntoView){
41940         this.selectedIndex = index;
41941         this.view.select(index);
41942         if(scrollIntoView !== false){
41943             var el = this.view.getNode(index);
41944             if(el){
41945                 this.innerList.scrollChildIntoView(el, false);
41946             }
41947         }
41948     },
41949
41950     // private
41951     selectNext : function(){
41952         var ct = this.store.getCount();
41953         if(ct > 0){
41954             if(this.selectedIndex == -1){
41955                 this.select(0);
41956             }else if(this.selectedIndex < ct-1){
41957                 this.select(this.selectedIndex+1);
41958             }
41959         }
41960     },
41961
41962     // private
41963     selectPrev : function(){
41964         var ct = this.store.getCount();
41965         if(ct > 0){
41966             if(this.selectedIndex == -1){
41967                 this.select(0);
41968             }else if(this.selectedIndex != 0){
41969                 this.select(this.selectedIndex-1);
41970             }
41971         }
41972     },
41973
41974     // private
41975     onKeyUp : function(e){
41976         if(this.editable !== false && !e.isSpecialKey()){
41977             this.lastKey = e.getKey();
41978             this.dqTask.delay(this.queryDelay);
41979         }
41980     },
41981
41982     // private
41983     validateBlur : function(){
41984         return !this.list || !this.list.isVisible();   
41985     },
41986
41987     // private
41988     initQuery : function(){
41989         this.doQuery(this.getRawValue());
41990     },
41991
41992     // private
41993     doForce : function(){
41994         if(this.el.dom.value.length > 0){
41995             this.el.dom.value =
41996                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41997              
41998         }
41999     },
42000
42001     /**
42002      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42003      * query allowing the query action to be canceled if needed.
42004      * @param {String} query The SQL query to execute
42005      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42006      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42007      * saved in the current store (defaults to false)
42008      */
42009     doQuery : function(q, forceAll){
42010         if(q === undefined || q === null){
42011             q = '';
42012         }
42013         var qe = {
42014             query: q,
42015             forceAll: forceAll,
42016             combo: this,
42017             cancel:false
42018         };
42019         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42020             return false;
42021         }
42022         q = qe.query;
42023         forceAll = qe.forceAll;
42024         if(forceAll === true || (q.length >= this.minChars)){
42025             if(this.lastQuery != q || this.alwaysQuery){
42026                 this.lastQuery = q;
42027                 if(this.mode == 'local'){
42028                     this.selectedIndex = -1;
42029                     if(forceAll){
42030                         this.store.clearFilter();
42031                     }else{
42032                         this.store.filter(this.displayField, q);
42033                     }
42034                     this.onLoad();
42035                 }else{
42036                     this.store.baseParams[this.queryParam] = q;
42037                     this.store.load({
42038                         params: this.getParams(q)
42039                     });
42040                     this.expand();
42041                 }
42042             }else{
42043                 this.selectedIndex = -1;
42044                 this.onLoad();   
42045             }
42046         }
42047     },
42048
42049     // private
42050     getParams : function(q){
42051         var p = {};
42052         //p[this.queryParam] = q;
42053         if(this.pageSize){
42054             p.start = 0;
42055             p.limit = this.pageSize;
42056         }
42057         return p;
42058     },
42059
42060     /**
42061      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42062      */
42063     collapse : function(){
42064         if(!this.isExpanded()){
42065             return;
42066         }
42067         this.list.hide();
42068         Roo.get(document).un('mousedown', this.collapseIf, this);
42069         Roo.get(document).un('mousewheel', this.collapseIf, this);
42070         if (!this.editable) {
42071             Roo.get(document).un('keydown', this.listKeyPress, this);
42072         }
42073         this.fireEvent('collapse', this);
42074     },
42075
42076     // private
42077     collapseIf : function(e){
42078         if(!e.within(this.wrap) && !e.within(this.list)){
42079             this.collapse();
42080         }
42081     },
42082
42083     /**
42084      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42085      */
42086     expand : function(){
42087         if(this.isExpanded() || !this.hasFocus){
42088             return;
42089         }
42090         this.list.alignTo(this.el, this.listAlign);
42091         this.list.show();
42092         Roo.get(document).on('mousedown', this.collapseIf, this);
42093         Roo.get(document).on('mousewheel', this.collapseIf, this);
42094         if (!this.editable) {
42095             Roo.get(document).on('keydown', this.listKeyPress, this);
42096         }
42097         
42098         this.fireEvent('expand', this);
42099     },
42100
42101     // private
42102     // Implements the default empty TriggerField.onTriggerClick function
42103     onTriggerClick : function(){
42104         if(this.disabled){
42105             return;
42106         }
42107         if(this.isExpanded()){
42108             this.collapse();
42109             if (!this.blockFocus) {
42110                 this.el.focus();
42111             }
42112             
42113         }else {
42114             this.hasFocus = true;
42115             if(this.triggerAction == 'all') {
42116                 this.doQuery(this.allQuery, true);
42117             } else {
42118                 this.doQuery(this.getRawValue());
42119             }
42120             if (!this.blockFocus) {
42121                 this.el.focus();
42122             }
42123         }
42124     },
42125     listKeyPress : function(e)
42126     {
42127         //Roo.log('listkeypress');
42128         // scroll to first matching element based on key pres..
42129         if (e.isSpecialKey()) {
42130             return false;
42131         }
42132         var k = String.fromCharCode(e.getKey()).toUpperCase();
42133         //Roo.log(k);
42134         var match  = false;
42135         var csel = this.view.getSelectedNodes();
42136         var cselitem = false;
42137         if (csel.length) {
42138             var ix = this.view.indexOf(csel[0]);
42139             cselitem  = this.store.getAt(ix);
42140             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42141                 cselitem = false;
42142             }
42143             
42144         }
42145         
42146         this.store.each(function(v) { 
42147             if (cselitem) {
42148                 // start at existing selection.
42149                 if (cselitem.id == v.id) {
42150                     cselitem = false;
42151                 }
42152                 return;
42153             }
42154                 
42155             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42156                 match = this.store.indexOf(v);
42157                 return false;
42158             }
42159         }, this);
42160         
42161         if (match === false) {
42162             return true; // no more action?
42163         }
42164         // scroll to?
42165         this.view.select(match);
42166         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42167         sn.scrollIntoView(sn.dom.parentNode, false);
42168     } 
42169
42170     /** 
42171     * @cfg {Boolean} grow 
42172     * @hide 
42173     */
42174     /** 
42175     * @cfg {Number} growMin 
42176     * @hide 
42177     */
42178     /** 
42179     * @cfg {Number} growMax 
42180     * @hide 
42181     */
42182     /**
42183      * @hide
42184      * @method autoSize
42185      */
42186 });/*
42187  * Copyright(c) 2010-2012, Roo J Solutions Limited
42188  *
42189  * Licence LGPL
42190  *
42191  */
42192
42193 /**
42194  * @class Roo.form.ComboBoxArray
42195  * @extends Roo.form.TextField
42196  * A facebook style adder... for lists of email / people / countries  etc...
42197  * pick multiple items from a combo box, and shows each one.
42198  *
42199  *  Fred [x]  Brian [x]  [Pick another |v]
42200  *
42201  *
42202  *  For this to work: it needs various extra information
42203  *    - normal combo problay has
42204  *      name, hiddenName
42205  *    + displayField, valueField
42206  *
42207  *    For our purpose...
42208  *
42209  *
42210  *   If we change from 'extends' to wrapping...
42211  *   
42212  *  
42213  *
42214  
42215  
42216  * @constructor
42217  * Create a new ComboBoxArray.
42218  * @param {Object} config Configuration options
42219  */
42220  
42221
42222 Roo.form.ComboBoxArray = function(config)
42223 {
42224     this.addEvents({
42225         /**
42226          * @event beforeremove
42227          * Fires before remove the value from the list
42228              * @param {Roo.form.ComboBoxArray} _self This combo box array
42229              * @param {Roo.form.ComboBoxArray.Item} item removed item
42230              */
42231         'beforeremove' : true,
42232         /**
42233          * @event remove
42234          * Fires when remove the value from the list
42235              * @param {Roo.form.ComboBoxArray} _self This combo box array
42236              * @param {Roo.form.ComboBoxArray.Item} item removed item
42237              */
42238         'remove' : true
42239         
42240         
42241     });
42242     
42243     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42244     
42245     this.items = new Roo.util.MixedCollection(false);
42246     
42247     // construct the child combo...
42248     
42249     
42250     
42251     
42252    
42253     
42254 }
42255
42256  
42257 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42258
42259     /**
42260      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42261      */
42262     
42263     lastData : false,
42264     
42265     // behavies liek a hiddne field
42266     inputType:      'hidden',
42267     /**
42268      * @cfg {Number} width The width of the box that displays the selected element
42269      */ 
42270     width:          300,
42271
42272     
42273     
42274     /**
42275      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42276      */
42277     name : false,
42278     /**
42279      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42280      */
42281     hiddenName : false,
42282     
42283     
42284     // private the array of items that are displayed..
42285     items  : false,
42286     // private - the hidden field el.
42287     hiddenEl : false,
42288     // private - the filed el..
42289     el : false,
42290     
42291     //validateValue : function() { return true; }, // all values are ok!
42292     //onAddClick: function() { },
42293     
42294     onRender : function(ct, position) 
42295     {
42296         
42297         // create the standard hidden element
42298         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42299         
42300         
42301         // give fake names to child combo;
42302         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42303         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42304         
42305         this.combo = Roo.factory(this.combo, Roo.form);
42306         this.combo.onRender(ct, position);
42307         if (typeof(this.combo.width) != 'undefined') {
42308             this.combo.onResize(this.combo.width,0);
42309         }
42310         
42311         this.combo.initEvents();
42312         
42313         // assigned so form know we need to do this..
42314         this.store          = this.combo.store;
42315         this.valueField     = this.combo.valueField;
42316         this.displayField   = this.combo.displayField ;
42317         
42318         
42319         this.combo.wrap.addClass('x-cbarray-grp');
42320         
42321         var cbwrap = this.combo.wrap.createChild(
42322             {tag: 'div', cls: 'x-cbarray-cb'},
42323             this.combo.el.dom
42324         );
42325         
42326              
42327         this.hiddenEl = this.combo.wrap.createChild({
42328             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42329         });
42330         this.el = this.combo.wrap.createChild({
42331             tag: 'input',  type:'hidden' , name: this.name, value : ''
42332         });
42333          //   this.el.dom.removeAttribute("name");
42334         
42335         
42336         this.outerWrap = this.combo.wrap;
42337         this.wrap = cbwrap;
42338         
42339         this.outerWrap.setWidth(this.width);
42340         this.outerWrap.dom.removeChild(this.el.dom);
42341         
42342         this.wrap.dom.appendChild(this.el.dom);
42343         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42344         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42345         
42346         this.combo.trigger.setStyle('position','relative');
42347         this.combo.trigger.setStyle('left', '0px');
42348         this.combo.trigger.setStyle('top', '2px');
42349         
42350         this.combo.el.setStyle('vertical-align', 'text-bottom');
42351         
42352         //this.trigger.setStyle('vertical-align', 'top');
42353         
42354         // this should use the code from combo really... on('add' ....)
42355         if (this.adder) {
42356             
42357         
42358             this.adder = this.outerWrap.createChild(
42359                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42360             var _t = this;
42361             this.adder.on('click', function(e) {
42362                 _t.fireEvent('adderclick', this, e);
42363             }, _t);
42364         }
42365         //var _t = this;
42366         //this.adder.on('click', this.onAddClick, _t);
42367         
42368         
42369         this.combo.on('select', function(cb, rec, ix) {
42370             this.addItem(rec.data);
42371             
42372             cb.setValue('');
42373             cb.el.dom.value = '';
42374             //cb.lastData = rec.data;
42375             // add to list
42376             
42377         }, this);
42378         
42379         
42380     },
42381     
42382     
42383     getName: function()
42384     {
42385         // returns hidden if it's set..
42386         if (!this.rendered) {return ''};
42387         return  this.hiddenName ? this.hiddenName : this.name;
42388         
42389     },
42390     
42391     
42392     onResize: function(w, h){
42393         
42394         return;
42395         // not sure if this is needed..
42396         //this.combo.onResize(w,h);
42397         
42398         if(typeof w != 'number'){
42399             // we do not handle it!?!?
42400             return;
42401         }
42402         var tw = this.combo.trigger.getWidth();
42403         tw += this.addicon ? this.addicon.getWidth() : 0;
42404         tw += this.editicon ? this.editicon.getWidth() : 0;
42405         var x = w - tw;
42406         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42407             
42408         this.combo.trigger.setStyle('left', '0px');
42409         
42410         if(this.list && this.listWidth === undefined){
42411             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42412             this.list.setWidth(lw);
42413             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42414         }
42415         
42416     
42417         
42418     },
42419     
42420     addItem: function(rec)
42421     {
42422         var valueField = this.combo.valueField;
42423         var displayField = this.combo.displayField;
42424         
42425         if (this.items.indexOfKey(rec[valueField]) > -1) {
42426             //console.log("GOT " + rec.data.id);
42427             return;
42428         }
42429         
42430         var x = new Roo.form.ComboBoxArray.Item({
42431             //id : rec[this.idField],
42432             data : rec,
42433             displayField : displayField ,
42434             tipField : displayField ,
42435             cb : this
42436         });
42437         // use the 
42438         this.items.add(rec[valueField],x);
42439         // add it before the element..
42440         this.updateHiddenEl();
42441         x.render(this.outerWrap, this.wrap.dom);
42442         // add the image handler..
42443     },
42444     
42445     updateHiddenEl : function()
42446     {
42447         this.validate();
42448         if (!this.hiddenEl) {
42449             return;
42450         }
42451         var ar = [];
42452         var idField = this.combo.valueField;
42453         
42454         this.items.each(function(f) {
42455             ar.push(f.data[idField]);
42456         });
42457         this.hiddenEl.dom.value = ar.join(',');
42458         this.validate();
42459     },
42460     
42461     reset : function()
42462     {
42463         this.items.clear();
42464         
42465         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42466            el.remove();
42467         });
42468         
42469         this.el.dom.value = '';
42470         if (this.hiddenEl) {
42471             this.hiddenEl.dom.value = '';
42472         }
42473         
42474     },
42475     getValue: function()
42476     {
42477         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42478     },
42479     setValue: function(v) // not a valid action - must use addItems..
42480     {
42481         
42482         this.reset();
42483          
42484         if (this.store.isLocal && (typeof(v) == 'string')) {
42485             // then we can use the store to find the values..
42486             // comma seperated at present.. this needs to allow JSON based encoding..
42487             this.hiddenEl.value  = v;
42488             var v_ar = [];
42489             Roo.each(v.split(','), function(k) {
42490                 Roo.log("CHECK " + this.valueField + ',' + k);
42491                 var li = this.store.query(this.valueField, k);
42492                 if (!li.length) {
42493                     return;
42494                 }
42495                 var add = {};
42496                 add[this.valueField] = k;
42497                 add[this.displayField] = li.item(0).data[this.displayField];
42498                 
42499                 this.addItem(add);
42500             }, this) 
42501              
42502         }
42503         if (typeof(v) == 'object' ) {
42504             // then let's assume it's an array of objects..
42505             Roo.each(v, function(l) {
42506                 this.addItem(l);
42507             }, this);
42508              
42509         }
42510         
42511         
42512     },
42513     setFromData: function(v)
42514     {
42515         // this recieves an object, if setValues is called.
42516         this.reset();
42517         this.el.dom.value = v[this.displayField];
42518         this.hiddenEl.dom.value = v[this.valueField];
42519         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42520             return;
42521         }
42522         var kv = v[this.valueField];
42523         var dv = v[this.displayField];
42524         kv = typeof(kv) != 'string' ? '' : kv;
42525         dv = typeof(dv) != 'string' ? '' : dv;
42526         
42527         
42528         var keys = kv.split(',');
42529         var display = dv.split(',');
42530         for (var i = 0 ; i < keys.length; i++) {
42531             
42532             add = {};
42533             add[this.valueField] = keys[i];
42534             add[this.displayField] = display[i];
42535             this.addItem(add);
42536         }
42537       
42538         
42539     },
42540     
42541     /**
42542      * Validates the combox array value
42543      * @return {Boolean} True if the value is valid, else false
42544      */
42545     validate : function(){
42546         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42547             this.clearInvalid();
42548             return true;
42549         }
42550         return false;
42551     },
42552     
42553     validateValue : function(value){
42554         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42555         
42556     },
42557     
42558     /*@
42559      * overide
42560      * 
42561      */
42562     isDirty : function() {
42563         if(this.disabled) {
42564             return false;
42565         }
42566         
42567         try {
42568             var d = Roo.decode(String(this.originalValue));
42569         } catch (e) {
42570             return String(this.getValue()) !== String(this.originalValue);
42571         }
42572         
42573         var originalValue = [];
42574         
42575         for (var i = 0; i < d.length; i++){
42576             originalValue.push(d[i][this.valueField]);
42577         }
42578         
42579         return String(this.getValue()) !== String(originalValue.join(','));
42580         
42581     }
42582     
42583 });
42584
42585
42586
42587 /**
42588  * @class Roo.form.ComboBoxArray.Item
42589  * @extends Roo.BoxComponent
42590  * A selected item in the list
42591  *  Fred [x]  Brian [x]  [Pick another |v]
42592  * 
42593  * @constructor
42594  * Create a new item.
42595  * @param {Object} config Configuration options
42596  */
42597  
42598 Roo.form.ComboBoxArray.Item = function(config) {
42599     config.id = Roo.id();
42600     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42601 }
42602
42603 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42604     data : {},
42605     cb: false,
42606     displayField : false,
42607     tipField : false,
42608     
42609     
42610     defaultAutoCreate : {
42611         tag: 'div',
42612         cls: 'x-cbarray-item',
42613         cn : [ 
42614             { tag: 'div' },
42615             {
42616                 tag: 'img',
42617                 width:16,
42618                 height : 16,
42619                 src : Roo.BLANK_IMAGE_URL ,
42620                 align: 'center'
42621             }
42622         ]
42623         
42624     },
42625     
42626  
42627     onRender : function(ct, position)
42628     {
42629         Roo.form.Field.superclass.onRender.call(this, ct, position);
42630         
42631         if(!this.el){
42632             var cfg = this.getAutoCreate();
42633             this.el = ct.createChild(cfg, position);
42634         }
42635         
42636         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42637         
42638         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42639             this.cb.renderer(this.data) :
42640             String.format('{0}',this.data[this.displayField]);
42641         
42642             
42643         this.el.child('div').dom.setAttribute('qtip',
42644                         String.format('{0}',this.data[this.tipField])
42645         );
42646         
42647         this.el.child('img').on('click', this.remove, this);
42648         
42649     },
42650    
42651     remove : function()
42652     {
42653         if(this.cb.disabled){
42654             return;
42655         }
42656         
42657         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42658             this.cb.items.remove(this);
42659             this.el.child('img').un('click', this.remove, this);
42660             this.el.remove();
42661             this.cb.updateHiddenEl();
42662
42663             this.cb.fireEvent('remove', this.cb, this);
42664         }
42665         
42666     }
42667 });/*
42668  * RooJS Library 1.1.1
42669  * Copyright(c) 2008-2011  Alan Knowles
42670  *
42671  * License - LGPL
42672  */
42673  
42674
42675 /**
42676  * @class Roo.form.ComboNested
42677  * @extends Roo.form.ComboBox
42678  * A combobox for that allows selection of nested items in a list,
42679  * eg.
42680  *
42681  *  Book
42682  *    -> red
42683  *    -> green
42684  *  Table
42685  *    -> square
42686  *      ->red
42687  *      ->green
42688  *    -> rectangle
42689  *      ->green
42690  *      
42691  * 
42692  * @constructor
42693  * Create a new ComboNested
42694  * @param {Object} config Configuration options
42695  */
42696 Roo.form.ComboNested = function(config){
42697     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42698     // should verify some data...
42699     // like
42700     // hiddenName = required..
42701     // displayField = required
42702     // valudField == required
42703     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42704     var _t = this;
42705     Roo.each(req, function(e) {
42706         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42707             throw "Roo.form.ComboNested : missing value for: " + e;
42708         }
42709     });
42710      
42711     
42712 };
42713
42714 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42715    
42716    
42717     list : null, // the outermost div..
42718     innerLists : null, // the
42719     views : null,
42720     stores : null,
42721     // private
42722     onRender : function(ct, position)
42723     {
42724         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
42725         
42726         if(this.hiddenName){
42727             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42728                     'before', true);
42729             this.hiddenField.value =
42730                 this.hiddenValue !== undefined ? this.hiddenValue :
42731                 this.value !== undefined ? this.value : '';
42732
42733             // prevent input submission
42734             this.el.dom.removeAttribute('name');
42735              
42736              
42737         }
42738         
42739         if(Roo.isGecko){
42740             this.el.dom.setAttribute('autocomplete', 'off');
42741         }
42742
42743         var cls = 'x-combo-list';
42744
42745         this.list = new Roo.Layer({
42746             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42747         });
42748
42749         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42750         this.list.setWidth(lw);
42751         this.list.swallowEvent('mousewheel');
42752         this.assetHeight = 0;
42753
42754         if(this.title){
42755             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42756             this.assetHeight += this.header.getHeight();
42757         }
42758         this.innerLists = [];
42759         this.views = [];
42760         this.stores = [];
42761         for (var i =0 ; i < 3; i++) {
42762             this.onRenderList( cls, i);
42763         }
42764         
42765         // always needs footer, as we are going to have an 'OK' button.
42766         this.footer = this.list.createChild({cls:cls+'-ft'});
42767         this.pageTb = new Roo.Toolbar(this.footer);  
42768         var _this = this;
42769         this.pageTb.add(  {
42770             
42771             text: 'Done',
42772             handler: function()
42773             {
42774                 _this.collapse();
42775             }
42776         });
42777         
42778         if ( this.allowBlank && !this.disableClear) {
42779             
42780             this.pageTb.add(new Roo.Toolbar.Fill(), {
42781                 cls: 'x-btn-icon x-btn-clear',
42782                 text: '&#160;',
42783                 handler: function()
42784                 {
42785                     _this.collapse();
42786                     _this.clearValue();
42787                     _this.onSelect(false, -1);
42788                 }
42789             });
42790         }
42791         if (this.footer) {
42792             this.assetHeight += this.footer.getHeight();
42793         }
42794         
42795     },
42796     onRenderList : function (  cls, i)
42797     {
42798         
42799         var lw = Math.floor(
42800                 ((this.listWidth * 3 || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / 3
42801         );
42802         
42803         this.list.setWidth(lw); // default to '1'
42804
42805         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
42806         //il.on('mouseover', this.onViewOver, this, { list:  i });
42807         //il.on('mousemove', this.onViewMove, this, { list:  i });
42808         il.setWidth(lw);
42809         il.setStyle({ 'overflow-x' : 'hidden'});
42810
42811         if(!this.tpl){
42812             this.tpl = new Roo.Template({
42813                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
42814                 isEmpty: function (value, allValues) {
42815                     return value.length ? 'has-children' : 'no-children'
42816                 }
42817             });
42818         }
42819         
42820         var store  = this.store;
42821         if (i > 0) {
42822             store  = new Roo.data.SimpleStore({
42823                 reader : this.store.reader,
42824                 data : [ ]
42825             });
42826         }
42827         this.stores[i]  = store;
42828                 
42829         
42830         
42831         var view = this.views[i] = new Roo.View(
42832             il,
42833             this.tpl,
42834             {
42835                 singleSelect:true,
42836                 store: store,
42837                 selectedClass: this.selectedClass
42838             }
42839         );
42840         view.getEl().setWidth(lw);
42841         view.getEl().setStyle({
42842             position: i < 1 ? 'relative' : 'absolute',
42843             top: 0,
42844             left: (i * lw ) + 'px',
42845             display : i > 0 ? 'none' : 'block'
42846         });
42847         view.on('selectionchange', this.onSelectChange, this, {list : i });
42848         view.on('dblclick', this.onDoubleClick, this, {list : i });
42849         //view.on('click', this.onViewClick, this, { list : i });
42850
42851         store.on('beforeload', this.onBeforeLoad, this);
42852         store.on('load',  this.onStoreLoad, this, { list  : i});
42853         store.on('loadexception', this.onLoadException, this);
42854
42855         // hide the other vies..
42856         
42857         
42858         
42859     },
42860     onResize : function()  {},
42861     
42862     restrictHeight : function()
42863     {
42864         var mh = 0;
42865         Roo.each(this.innerLists, function(il,i) {
42866             var el = this.views[i].getEl();
42867             el.dom.style.height = '';
42868             var inner = el.dom;
42869             var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42870             // only adjust heights on other ones..
42871             if (i < 1) {
42872                 
42873                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42874                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42875                 mh = Math.max(el.getHeight(), mh);
42876             }
42877             
42878             
42879         }, this);
42880         
42881         this.list.beginUpdate();
42882         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
42883         this.list.alignTo(this.el, this.listAlign);
42884         this.list.endUpdate();
42885         
42886     },
42887      
42888     
42889     // -- store handlers..
42890     
42891     // private
42892     onLoad : function(a,b,c,d)
42893     {
42894         
42895         if(!this.hasFocus){
42896             return;
42897         }
42898         
42899         if(this.store.getCount() > 0) {
42900             this.expand();
42901             this.restrictHeight();   
42902         } else {
42903             this.onEmptyResults();
42904         }
42905         /*
42906         this.stores[1].loadData([]);
42907         this.stores[2].loadData([]);
42908         this.views
42909         */    
42910     
42911         //this.el.focus();
42912     },
42913     onStoreLoad : function ()
42914     {
42915         Roo.log(arguments);
42916     },
42917     
42918     // private
42919     onLoadException : function()
42920     {
42921         this.collapse();
42922         Roo.log(this.store.reader.jsonData);
42923         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42924             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42925         }
42926         
42927         
42928     } ,
42929      
42930      
42931
42932     onSelectChange : function (view, sels, opts )
42933     {
42934         var ix = view.getSelectedIndexes();
42935         
42936         
42937         if (opts.list > 1) {
42938              
42939             this.setFromData(ix.length ? view.store.getAt(ix[0]).data : {});
42940             return;
42941         }
42942         
42943         if (!ix.length) {
42944             this.setFromData({});
42945             this.stores[opts.list+1].loadData( [] );
42946             return;
42947         }
42948         
42949         var rec = view.store.getAt(ix[0]);
42950         this.setFromData(rec.data);
42951         
42952         var lw = Math.floor(
42953                 ((this.listWidth * 3 || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / 3
42954         );
42955         
42956         this.stores[opts.list+1].loadData( typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn);
42957         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
42958         this.views[opts.list+1].getEl().setStyle({ display : rec.data.cn.length ? 'block' : 'none' });
42959         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
42960         this.list.setWidth(lw * (opts.list + (rec.data.cn.length ? 2 : 1))); 
42961     },
42962     onDoubleClick : function()
42963     {
42964         this.collapse(); //??
42965     },
42966     
42967      
42968     
42969     findRecord : function (prop,value)
42970     {
42971         return this.findRecordInStore(this.store, prop,value);
42972     },
42973     
42974      // private
42975     findRecordInStore : function(store, prop, value)
42976     {
42977         var cstore = new Roo.data.SimpleStore({
42978             reader : this.store.reader,
42979             data : [ ]
42980         });
42981         var _this = this;
42982         var record  = false;
42983         if(store.getCount() > 0){
42984            store.each(function(r){
42985                 if(r.data[prop] == value){
42986                     record = r;
42987                     return false;
42988                 }
42989                 if (r.data.cn && r.data.cn.length) {
42990                     cstore.loadData( r.data.cn);
42991                     var cret = _this.findRecordInStore(cstore, prop, value);
42992                     if (cret !== false) {
42993                         record = cret;
42994                         return false;
42995                     }
42996                 }
42997                 
42998                 return true;
42999             });
43000         }
43001         return record;
43002     }
43003     
43004 });/*
43005  * Based on:
43006  * Ext JS Library 1.1.1
43007  * Copyright(c) 2006-2007, Ext JS, LLC.
43008  *
43009  * Originally Released Under LGPL - original licence link has changed is not relivant.
43010  *
43011  * Fork - LGPL
43012  * <script type="text/javascript">
43013  */
43014 /**
43015  * @class Roo.form.Checkbox
43016  * @extends Roo.form.Field
43017  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43018  * @constructor
43019  * Creates a new Checkbox
43020  * @param {Object} config Configuration options
43021  */
43022 Roo.form.Checkbox = function(config){
43023     Roo.form.Checkbox.superclass.constructor.call(this, config);
43024     this.addEvents({
43025         /**
43026          * @event check
43027          * Fires when the checkbox is checked or unchecked.
43028              * @param {Roo.form.Checkbox} this This checkbox
43029              * @param {Boolean} checked The new checked value
43030              */
43031         check : true
43032     });
43033 };
43034
43035 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43036     /**
43037      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43038      */
43039     focusClass : undefined,
43040     /**
43041      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43042      */
43043     fieldClass: "x-form-field",
43044     /**
43045      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43046      */
43047     checked: false,
43048     /**
43049      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43050      * {tag: "input", type: "checkbox", autocomplete: "off"})
43051      */
43052     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43053     /**
43054      * @cfg {String} boxLabel The text that appears beside the checkbox
43055      */
43056     boxLabel : "",
43057     /**
43058      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43059      */  
43060     inputValue : '1',
43061     /**
43062      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43063      */
43064      valueOff: '0', // value when not checked..
43065
43066     actionMode : 'viewEl', 
43067     //
43068     // private
43069     itemCls : 'x-menu-check-item x-form-item',
43070     groupClass : 'x-menu-group-item',
43071     inputType : 'hidden',
43072     
43073     
43074     inSetChecked: false, // check that we are not calling self...
43075     
43076     inputElement: false, // real input element?
43077     basedOn: false, // ????
43078     
43079     isFormField: true, // not sure where this is needed!!!!
43080
43081     onResize : function(){
43082         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43083         if(!this.boxLabel){
43084             this.el.alignTo(this.wrap, 'c-c');
43085         }
43086     },
43087
43088     initEvents : function(){
43089         Roo.form.Checkbox.superclass.initEvents.call(this);
43090         this.el.on("click", this.onClick,  this);
43091         this.el.on("change", this.onClick,  this);
43092     },
43093
43094
43095     getResizeEl : function(){
43096         return this.wrap;
43097     },
43098
43099     getPositionEl : function(){
43100         return this.wrap;
43101     },
43102
43103     // private
43104     onRender : function(ct, position){
43105         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43106         /*
43107         if(this.inputValue !== undefined){
43108             this.el.dom.value = this.inputValue;
43109         }
43110         */
43111         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43112         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43113         var viewEl = this.wrap.createChild({ 
43114             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43115         this.viewEl = viewEl;   
43116         this.wrap.on('click', this.onClick,  this); 
43117         
43118         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43119         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43120         
43121         
43122         
43123         if(this.boxLabel){
43124             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43125         //    viewEl.on('click', this.onClick,  this); 
43126         }
43127         //if(this.checked){
43128             this.setChecked(this.checked);
43129         //}else{
43130             //this.checked = this.el.dom;
43131         //}
43132
43133     },
43134
43135     // private
43136     initValue : Roo.emptyFn,
43137
43138     /**
43139      * Returns the checked state of the checkbox.
43140      * @return {Boolean} True if checked, else false
43141      */
43142     getValue : function(){
43143         if(this.el){
43144             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43145         }
43146         return this.valueOff;
43147         
43148     },
43149
43150         // private
43151     onClick : function(){ 
43152         if (this.disabled) {
43153             return;
43154         }
43155         this.setChecked(!this.checked);
43156
43157         //if(this.el.dom.checked != this.checked){
43158         //    this.setValue(this.el.dom.checked);
43159        // }
43160     },
43161
43162     /**
43163      * Sets the checked state of the checkbox.
43164      * On is always based on a string comparison between inputValue and the param.
43165      * @param {Boolean/String} value - the value to set 
43166      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43167      */
43168     setValue : function(v,suppressEvent){
43169         
43170         
43171         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43172         //if(this.el && this.el.dom){
43173         //    this.el.dom.checked = this.checked;
43174         //    this.el.dom.defaultChecked = this.checked;
43175         //}
43176         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43177         //this.fireEvent("check", this, this.checked);
43178     },
43179     // private..
43180     setChecked : function(state,suppressEvent)
43181     {
43182         if (this.inSetChecked) {
43183             this.checked = state;
43184             return;
43185         }
43186         
43187     
43188         if(this.wrap){
43189             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43190         }
43191         this.checked = state;
43192         if(suppressEvent !== true){
43193             this.fireEvent('check', this, state);
43194         }
43195         this.inSetChecked = true;
43196         this.el.dom.value = state ? this.inputValue : this.valueOff;
43197         this.inSetChecked = false;
43198         
43199     },
43200     // handle setting of hidden value by some other method!!?!?
43201     setFromHidden: function()
43202     {
43203         if(!this.el){
43204             return;
43205         }
43206         //console.log("SET FROM HIDDEN");
43207         //alert('setFrom hidden');
43208         this.setValue(this.el.dom.value);
43209     },
43210     
43211     onDestroy : function()
43212     {
43213         if(this.viewEl){
43214             Roo.get(this.viewEl).remove();
43215         }
43216          
43217         Roo.form.Checkbox.superclass.onDestroy.call(this);
43218     },
43219     
43220     setBoxLabel : function(str)
43221     {
43222         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43223     }
43224
43225 });/*
43226  * Based on:
43227  * Ext JS Library 1.1.1
43228  * Copyright(c) 2006-2007, Ext JS, LLC.
43229  *
43230  * Originally Released Under LGPL - original licence link has changed is not relivant.
43231  *
43232  * Fork - LGPL
43233  * <script type="text/javascript">
43234  */
43235  
43236 /**
43237  * @class Roo.form.Radio
43238  * @extends Roo.form.Checkbox
43239  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43240  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43241  * @constructor
43242  * Creates a new Radio
43243  * @param {Object} config Configuration options
43244  */
43245 Roo.form.Radio = function(){
43246     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43247 };
43248 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43249     inputType: 'radio',
43250
43251     /**
43252      * If this radio is part of a group, it will return the selected value
43253      * @return {String}
43254      */
43255     getGroupValue : function(){
43256         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43257     },
43258     
43259     
43260     onRender : function(ct, position){
43261         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43262         
43263         if(this.inputValue !== undefined){
43264             this.el.dom.value = this.inputValue;
43265         }
43266          
43267         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43268         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43269         //var viewEl = this.wrap.createChild({ 
43270         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43271         //this.viewEl = viewEl;   
43272         //this.wrap.on('click', this.onClick,  this); 
43273         
43274         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43275         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43276         
43277         
43278         
43279         if(this.boxLabel){
43280             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43281         //    viewEl.on('click', this.onClick,  this); 
43282         }
43283          if(this.checked){
43284             this.el.dom.checked =   'checked' ;
43285         }
43286          
43287     } 
43288     
43289     
43290 });//<script type="text/javascript">
43291
43292 /*
43293  * Based  Ext JS Library 1.1.1
43294  * Copyright(c) 2006-2007, Ext JS, LLC.
43295  * LGPL
43296  *
43297  */
43298  
43299 /**
43300  * @class Roo.HtmlEditorCore
43301  * @extends Roo.Component
43302  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43303  *
43304  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43305  */
43306
43307 Roo.HtmlEditorCore = function(config){
43308     
43309     
43310     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43311     
43312     
43313     this.addEvents({
43314         /**
43315          * @event initialize
43316          * Fires when the editor is fully initialized (including the iframe)
43317          * @param {Roo.HtmlEditorCore} this
43318          */
43319         initialize: true,
43320         /**
43321          * @event activate
43322          * Fires when the editor is first receives the focus. Any insertion must wait
43323          * until after this event.
43324          * @param {Roo.HtmlEditorCore} this
43325          */
43326         activate: true,
43327          /**
43328          * @event beforesync
43329          * Fires before the textarea is updated with content from the editor iframe. Return false
43330          * to cancel the sync.
43331          * @param {Roo.HtmlEditorCore} this
43332          * @param {String} html
43333          */
43334         beforesync: true,
43335          /**
43336          * @event beforepush
43337          * Fires before the iframe editor is updated with content from the textarea. Return false
43338          * to cancel the push.
43339          * @param {Roo.HtmlEditorCore} this
43340          * @param {String} html
43341          */
43342         beforepush: true,
43343          /**
43344          * @event sync
43345          * Fires when the textarea is updated with content from the editor iframe.
43346          * @param {Roo.HtmlEditorCore} this
43347          * @param {String} html
43348          */
43349         sync: true,
43350          /**
43351          * @event push
43352          * Fires when the iframe editor is updated with content from the textarea.
43353          * @param {Roo.HtmlEditorCore} this
43354          * @param {String} html
43355          */
43356         push: true,
43357         
43358         /**
43359          * @event editorevent
43360          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43361          * @param {Roo.HtmlEditorCore} this
43362          */
43363         editorevent: true
43364         
43365     });
43366     
43367     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43368     
43369     // defaults : white / black...
43370     this.applyBlacklists();
43371     
43372     
43373     
43374 };
43375
43376
43377 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43378
43379
43380      /**
43381      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43382      */
43383     
43384     owner : false,
43385     
43386      /**
43387      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43388      *                        Roo.resizable.
43389      */
43390     resizable : false,
43391      /**
43392      * @cfg {Number} height (in pixels)
43393      */   
43394     height: 300,
43395    /**
43396      * @cfg {Number} width (in pixels)
43397      */   
43398     width: 500,
43399     
43400     /**
43401      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43402      * 
43403      */
43404     stylesheets: false,
43405     
43406     // id of frame..
43407     frameId: false,
43408     
43409     // private properties
43410     validationEvent : false,
43411     deferHeight: true,
43412     initialized : false,
43413     activated : false,
43414     sourceEditMode : false,
43415     onFocus : Roo.emptyFn,
43416     iframePad:3,
43417     hideMode:'offsets',
43418     
43419     clearUp: true,
43420     
43421     // blacklist + whitelisted elements..
43422     black: false,
43423     white: false,
43424      
43425     bodyCls : '',
43426
43427     /**
43428      * Protected method that will not generally be called directly. It
43429      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43430      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43431      */
43432     getDocMarkup : function(){
43433         // body styles..
43434         var st = '';
43435         
43436         // inherit styels from page...?? 
43437         if (this.stylesheets === false) {
43438             
43439             Roo.get(document.head).select('style').each(function(node) {
43440                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43441             });
43442             
43443             Roo.get(document.head).select('link').each(function(node) { 
43444                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43445             });
43446             
43447         } else if (!this.stylesheets.length) {
43448                 // simple..
43449                 st = '<style type="text/css">' +
43450                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43451                    '</style>';
43452         } else { 
43453             st = '<style type="text/css">' +
43454                     this.stylesheets +
43455                 '</style>';
43456         }
43457         
43458         st +=  '<style type="text/css">' +
43459             'IMG { cursor: pointer } ' +
43460         '</style>';
43461
43462         var cls = 'roo-htmleditor-body';
43463         
43464         if(this.bodyCls.length){
43465             cls += ' ' + this.bodyCls;
43466         }
43467         
43468         return '<html><head>' + st  +
43469             //<style type="text/css">' +
43470             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43471             //'</style>' +
43472             ' </head><body class="' +  cls + '"></body></html>';
43473     },
43474
43475     // private
43476     onRender : function(ct, position)
43477     {
43478         var _t = this;
43479         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43480         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43481         
43482         
43483         this.el.dom.style.border = '0 none';
43484         this.el.dom.setAttribute('tabIndex', -1);
43485         this.el.addClass('x-hidden hide');
43486         
43487         
43488         
43489         if(Roo.isIE){ // fix IE 1px bogus margin
43490             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43491         }
43492        
43493         
43494         this.frameId = Roo.id();
43495         
43496          
43497         
43498         var iframe = this.owner.wrap.createChild({
43499             tag: 'iframe',
43500             cls: 'form-control', // bootstrap..
43501             id: this.frameId,
43502             name: this.frameId,
43503             frameBorder : 'no',
43504             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43505         }, this.el
43506         );
43507         
43508         
43509         this.iframe = iframe.dom;
43510
43511          this.assignDocWin();
43512         
43513         this.doc.designMode = 'on';
43514        
43515         this.doc.open();
43516         this.doc.write(this.getDocMarkup());
43517         this.doc.close();
43518
43519         
43520         var task = { // must defer to wait for browser to be ready
43521             run : function(){
43522                 //console.log("run task?" + this.doc.readyState);
43523                 this.assignDocWin();
43524                 if(this.doc.body || this.doc.readyState == 'complete'){
43525                     try {
43526                         this.doc.designMode="on";
43527                     } catch (e) {
43528                         return;
43529                     }
43530                     Roo.TaskMgr.stop(task);
43531                     this.initEditor.defer(10, this);
43532                 }
43533             },
43534             interval : 10,
43535             duration: 10000,
43536             scope: this
43537         };
43538         Roo.TaskMgr.start(task);
43539
43540     },
43541
43542     // private
43543     onResize : function(w, h)
43544     {
43545          Roo.log('resize: ' +w + ',' + h );
43546         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43547         if(!this.iframe){
43548             return;
43549         }
43550         if(typeof w == 'number'){
43551             
43552             this.iframe.style.width = w + 'px';
43553         }
43554         if(typeof h == 'number'){
43555             
43556             this.iframe.style.height = h + 'px';
43557             if(this.doc){
43558                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43559             }
43560         }
43561         
43562     },
43563
43564     /**
43565      * Toggles the editor between standard and source edit mode.
43566      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43567      */
43568     toggleSourceEdit : function(sourceEditMode){
43569         
43570         this.sourceEditMode = sourceEditMode === true;
43571         
43572         if(this.sourceEditMode){
43573  
43574             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43575             
43576         }else{
43577             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43578             //this.iframe.className = '';
43579             this.deferFocus();
43580         }
43581         //this.setSize(this.owner.wrap.getSize());
43582         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43583     },
43584
43585     
43586   
43587
43588     /**
43589      * Protected method that will not generally be called directly. If you need/want
43590      * custom HTML cleanup, this is the method you should override.
43591      * @param {String} html The HTML to be cleaned
43592      * return {String} The cleaned HTML
43593      */
43594     cleanHtml : function(html){
43595         html = String(html);
43596         if(html.length > 5){
43597             if(Roo.isSafari){ // strip safari nonsense
43598                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43599             }
43600         }
43601         if(html == '&nbsp;'){
43602             html = '';
43603         }
43604         return html;
43605     },
43606
43607     /**
43608      * HTML Editor -> Textarea
43609      * Protected method that will not generally be called directly. Syncs the contents
43610      * of the editor iframe with the textarea.
43611      */
43612     syncValue : function(){
43613         if(this.initialized){
43614             var bd = (this.doc.body || this.doc.documentElement);
43615             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43616             var html = bd.innerHTML;
43617             if(Roo.isSafari){
43618                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43619                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43620                 if(m && m[1]){
43621                     html = '<div style="'+m[0]+'">' + html + '</div>';
43622                 }
43623             }
43624             html = this.cleanHtml(html);
43625             // fix up the special chars.. normaly like back quotes in word...
43626             // however we do not want to do this with chinese..
43627             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43628                 
43629                 var cc = match.charCodeAt();
43630
43631                 // Get the character value, handling surrogate pairs
43632                 if (match.length == 2) {
43633                     // It's a surrogate pair, calculate the Unicode code point
43634                     var high = match.charCodeAt(0) - 0xD800;
43635                     var low  = match.charCodeAt(1) - 0xDC00;
43636                     cc = (high * 0x400) + low + 0x10000;
43637                 }  else if (
43638                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43639                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43640                     (cc >= 0xf900 && cc < 0xfb00 )
43641                 ) {
43642                         return match;
43643                 }  
43644          
43645                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43646                 return "&#" + cc + ";";
43647                 
43648                 
43649             });
43650             
43651             
43652              
43653             if(this.owner.fireEvent('beforesync', this, html) !== false){
43654                 this.el.dom.value = html;
43655                 this.owner.fireEvent('sync', this, html);
43656             }
43657         }
43658     },
43659
43660     /**
43661      * Protected method that will not generally be called directly. Pushes the value of the textarea
43662      * into the iframe editor.
43663      */
43664     pushValue : function(){
43665         if(this.initialized){
43666             var v = this.el.dom.value.trim();
43667             
43668 //            if(v.length < 1){
43669 //                v = '&#160;';
43670 //            }
43671             
43672             if(this.owner.fireEvent('beforepush', this, v) !== false){
43673                 var d = (this.doc.body || this.doc.documentElement);
43674                 d.innerHTML = v;
43675                 this.cleanUpPaste();
43676                 this.el.dom.value = d.innerHTML;
43677                 this.owner.fireEvent('push', this, v);
43678             }
43679         }
43680     },
43681
43682     // private
43683     deferFocus : function(){
43684         this.focus.defer(10, this);
43685     },
43686
43687     // doc'ed in Field
43688     focus : function(){
43689         if(this.win && !this.sourceEditMode){
43690             this.win.focus();
43691         }else{
43692             this.el.focus();
43693         }
43694     },
43695     
43696     assignDocWin: function()
43697     {
43698         var iframe = this.iframe;
43699         
43700          if(Roo.isIE){
43701             this.doc = iframe.contentWindow.document;
43702             this.win = iframe.contentWindow;
43703         } else {
43704 //            if (!Roo.get(this.frameId)) {
43705 //                return;
43706 //            }
43707 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43708 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43709             
43710             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43711                 return;
43712             }
43713             
43714             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43715             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43716         }
43717     },
43718     
43719     // private
43720     initEditor : function(){
43721         //console.log("INIT EDITOR");
43722         this.assignDocWin();
43723         
43724         
43725         
43726         this.doc.designMode="on";
43727         this.doc.open();
43728         this.doc.write(this.getDocMarkup());
43729         this.doc.close();
43730         
43731         var dbody = (this.doc.body || this.doc.documentElement);
43732         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43733         // this copies styles from the containing element into thsi one..
43734         // not sure why we need all of this..
43735         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43736         
43737         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43738         //ss['background-attachment'] = 'fixed'; // w3c
43739         dbody.bgProperties = 'fixed'; // ie
43740         //Roo.DomHelper.applyStyles(dbody, ss);
43741         Roo.EventManager.on(this.doc, {
43742             //'mousedown': this.onEditorEvent,
43743             'mouseup': this.onEditorEvent,
43744             'dblclick': this.onEditorEvent,
43745             'click': this.onEditorEvent,
43746             'keyup': this.onEditorEvent,
43747             buffer:100,
43748             scope: this
43749         });
43750         if(Roo.isGecko){
43751             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43752         }
43753         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43754             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43755         }
43756         this.initialized = true;
43757
43758         this.owner.fireEvent('initialize', this);
43759         this.pushValue();
43760     },
43761
43762     // private
43763     onDestroy : function(){
43764         
43765         
43766         
43767         if(this.rendered){
43768             
43769             //for (var i =0; i < this.toolbars.length;i++) {
43770             //    // fixme - ask toolbars for heights?
43771             //    this.toolbars[i].onDestroy();
43772            // }
43773             
43774             //this.wrap.dom.innerHTML = '';
43775             //this.wrap.remove();
43776         }
43777     },
43778
43779     // private
43780     onFirstFocus : function(){
43781         
43782         this.assignDocWin();
43783         
43784         
43785         this.activated = true;
43786          
43787     
43788         if(Roo.isGecko){ // prevent silly gecko errors
43789             this.win.focus();
43790             var s = this.win.getSelection();
43791             if(!s.focusNode || s.focusNode.nodeType != 3){
43792                 var r = s.getRangeAt(0);
43793                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43794                 r.collapse(true);
43795                 this.deferFocus();
43796             }
43797             try{
43798                 this.execCmd('useCSS', true);
43799                 this.execCmd('styleWithCSS', false);
43800             }catch(e){}
43801         }
43802         this.owner.fireEvent('activate', this);
43803     },
43804
43805     // private
43806     adjustFont: function(btn){
43807         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43808         //if(Roo.isSafari){ // safari
43809         //    adjust *= 2;
43810        // }
43811         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43812         if(Roo.isSafari){ // safari
43813             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43814             v =  (v < 10) ? 10 : v;
43815             v =  (v > 48) ? 48 : v;
43816             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43817             
43818         }
43819         
43820         
43821         v = Math.max(1, v+adjust);
43822         
43823         this.execCmd('FontSize', v  );
43824     },
43825
43826     onEditorEvent : function(e)
43827     {
43828         this.owner.fireEvent('editorevent', this, e);
43829       //  this.updateToolbar();
43830         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43831     },
43832
43833     insertTag : function(tg)
43834     {
43835         // could be a bit smarter... -> wrap the current selected tRoo..
43836         if (tg.toLowerCase() == 'span' ||
43837             tg.toLowerCase() == 'code' ||
43838             tg.toLowerCase() == 'sup' ||
43839             tg.toLowerCase() == 'sub' 
43840             ) {
43841             
43842             range = this.createRange(this.getSelection());
43843             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43844             wrappingNode.appendChild(range.extractContents());
43845             range.insertNode(wrappingNode);
43846
43847             return;
43848             
43849             
43850             
43851         }
43852         this.execCmd("formatblock",   tg);
43853         
43854     },
43855     
43856     insertText : function(txt)
43857     {
43858         
43859         
43860         var range = this.createRange();
43861         range.deleteContents();
43862                //alert(Sender.getAttribute('label'));
43863                
43864         range.insertNode(this.doc.createTextNode(txt));
43865     } ,
43866     
43867      
43868
43869     /**
43870      * Executes a Midas editor command on the editor document and performs necessary focus and
43871      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43872      * @param {String} cmd The Midas command
43873      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43874      */
43875     relayCmd : function(cmd, value){
43876         this.win.focus();
43877         this.execCmd(cmd, value);
43878         this.owner.fireEvent('editorevent', this);
43879         //this.updateToolbar();
43880         this.owner.deferFocus();
43881     },
43882
43883     /**
43884      * Executes a Midas editor command directly on the editor document.
43885      * For visual commands, you should use {@link #relayCmd} instead.
43886      * <b>This should only be called after the editor is initialized.</b>
43887      * @param {String} cmd The Midas command
43888      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43889      */
43890     execCmd : function(cmd, value){
43891         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43892         this.syncValue();
43893     },
43894  
43895  
43896    
43897     /**
43898      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43899      * to insert tRoo.
43900      * @param {String} text | dom node.. 
43901      */
43902     insertAtCursor : function(text)
43903     {
43904         
43905         if(!this.activated){
43906             return;
43907         }
43908         /*
43909         if(Roo.isIE){
43910             this.win.focus();
43911             var r = this.doc.selection.createRange();
43912             if(r){
43913                 r.collapse(true);
43914                 r.pasteHTML(text);
43915                 this.syncValue();
43916                 this.deferFocus();
43917             
43918             }
43919             return;
43920         }
43921         */
43922         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43923             this.win.focus();
43924             
43925             
43926             // from jquery ui (MIT licenced)
43927             var range, node;
43928             var win = this.win;
43929             
43930             if (win.getSelection && win.getSelection().getRangeAt) {
43931                 range = win.getSelection().getRangeAt(0);
43932                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43933                 range.insertNode(node);
43934             } else if (win.document.selection && win.document.selection.createRange) {
43935                 // no firefox support
43936                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43937                 win.document.selection.createRange().pasteHTML(txt);
43938             } else {
43939                 // no firefox support
43940                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43941                 this.execCmd('InsertHTML', txt);
43942             } 
43943             
43944             this.syncValue();
43945             
43946             this.deferFocus();
43947         }
43948     },
43949  // private
43950     mozKeyPress : function(e){
43951         if(e.ctrlKey){
43952             var c = e.getCharCode(), cmd;
43953           
43954             if(c > 0){
43955                 c = String.fromCharCode(c).toLowerCase();
43956                 switch(c){
43957                     case 'b':
43958                         cmd = 'bold';
43959                         break;
43960                     case 'i':
43961                         cmd = 'italic';
43962                         break;
43963                     
43964                     case 'u':
43965                         cmd = 'underline';
43966                         break;
43967                     
43968                     case 'v':
43969                         this.cleanUpPaste.defer(100, this);
43970                         return;
43971                         
43972                 }
43973                 if(cmd){
43974                     this.win.focus();
43975                     this.execCmd(cmd);
43976                     this.deferFocus();
43977                     e.preventDefault();
43978                 }
43979                 
43980             }
43981         }
43982     },
43983
43984     // private
43985     fixKeys : function(){ // load time branching for fastest keydown performance
43986         if(Roo.isIE){
43987             return function(e){
43988                 var k = e.getKey(), r;
43989                 if(k == e.TAB){
43990                     e.stopEvent();
43991                     r = this.doc.selection.createRange();
43992                     if(r){
43993                         r.collapse(true);
43994                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43995                         this.deferFocus();
43996                     }
43997                     return;
43998                 }
43999                 
44000                 if(k == e.ENTER){
44001                     r = this.doc.selection.createRange();
44002                     if(r){
44003                         var target = r.parentElement();
44004                         if(!target || target.tagName.toLowerCase() != 'li'){
44005                             e.stopEvent();
44006                             r.pasteHTML('<br />');
44007                             r.collapse(false);
44008                             r.select();
44009                         }
44010                     }
44011                 }
44012                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44013                     this.cleanUpPaste.defer(100, this);
44014                     return;
44015                 }
44016                 
44017                 
44018             };
44019         }else if(Roo.isOpera){
44020             return function(e){
44021                 var k = e.getKey();
44022                 if(k == e.TAB){
44023                     e.stopEvent();
44024                     this.win.focus();
44025                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44026                     this.deferFocus();
44027                 }
44028                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44029                     this.cleanUpPaste.defer(100, this);
44030                     return;
44031                 }
44032                 
44033             };
44034         }else if(Roo.isSafari){
44035             return function(e){
44036                 var k = e.getKey();
44037                 
44038                 if(k == e.TAB){
44039                     e.stopEvent();
44040                     this.execCmd('InsertText','\t');
44041                     this.deferFocus();
44042                     return;
44043                 }
44044                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44045                     this.cleanUpPaste.defer(100, this);
44046                     return;
44047                 }
44048                 
44049              };
44050         }
44051     }(),
44052     
44053     getAllAncestors: function()
44054     {
44055         var p = this.getSelectedNode();
44056         var a = [];
44057         if (!p) {
44058             a.push(p); // push blank onto stack..
44059             p = this.getParentElement();
44060         }
44061         
44062         
44063         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44064             a.push(p);
44065             p = p.parentNode;
44066         }
44067         a.push(this.doc.body);
44068         return a;
44069     },
44070     lastSel : false,
44071     lastSelNode : false,
44072     
44073     
44074     getSelection : function() 
44075     {
44076         this.assignDocWin();
44077         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44078     },
44079     
44080     getSelectedNode: function() 
44081     {
44082         // this may only work on Gecko!!!
44083         
44084         // should we cache this!!!!
44085         
44086         
44087         
44088          
44089         var range = this.createRange(this.getSelection()).cloneRange();
44090         
44091         if (Roo.isIE) {
44092             var parent = range.parentElement();
44093             while (true) {
44094                 var testRange = range.duplicate();
44095                 testRange.moveToElementText(parent);
44096                 if (testRange.inRange(range)) {
44097                     break;
44098                 }
44099                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44100                     break;
44101                 }
44102                 parent = parent.parentElement;
44103             }
44104             return parent;
44105         }
44106         
44107         // is ancestor a text element.
44108         var ac =  range.commonAncestorContainer;
44109         if (ac.nodeType == 3) {
44110             ac = ac.parentNode;
44111         }
44112         
44113         var ar = ac.childNodes;
44114          
44115         var nodes = [];
44116         var other_nodes = [];
44117         var has_other_nodes = false;
44118         for (var i=0;i<ar.length;i++) {
44119             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44120                 continue;
44121             }
44122             // fullly contained node.
44123             
44124             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44125                 nodes.push(ar[i]);
44126                 continue;
44127             }
44128             
44129             // probably selected..
44130             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44131                 other_nodes.push(ar[i]);
44132                 continue;
44133             }
44134             // outer..
44135             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44136                 continue;
44137             }
44138             
44139             
44140             has_other_nodes = true;
44141         }
44142         if (!nodes.length && other_nodes.length) {
44143             nodes= other_nodes;
44144         }
44145         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44146             return false;
44147         }
44148         
44149         return nodes[0];
44150     },
44151     createRange: function(sel)
44152     {
44153         // this has strange effects when using with 
44154         // top toolbar - not sure if it's a great idea.
44155         //this.editor.contentWindow.focus();
44156         if (typeof sel != "undefined") {
44157             try {
44158                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44159             } catch(e) {
44160                 return this.doc.createRange();
44161             }
44162         } else {
44163             return this.doc.createRange();
44164         }
44165     },
44166     getParentElement: function()
44167     {
44168         
44169         this.assignDocWin();
44170         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44171         
44172         var range = this.createRange(sel);
44173          
44174         try {
44175             var p = range.commonAncestorContainer;
44176             while (p.nodeType == 3) { // text node
44177                 p = p.parentNode;
44178             }
44179             return p;
44180         } catch (e) {
44181             return null;
44182         }
44183     
44184     },
44185     /***
44186      *
44187      * Range intersection.. the hard stuff...
44188      *  '-1' = before
44189      *  '0' = hits..
44190      *  '1' = after.
44191      *         [ -- selected range --- ]
44192      *   [fail]                        [fail]
44193      *
44194      *    basically..
44195      *      if end is before start or  hits it. fail.
44196      *      if start is after end or hits it fail.
44197      *
44198      *   if either hits (but other is outside. - then it's not 
44199      *   
44200      *    
44201      **/
44202     
44203     
44204     // @see http://www.thismuchiknow.co.uk/?p=64.
44205     rangeIntersectsNode : function(range, node)
44206     {
44207         var nodeRange = node.ownerDocument.createRange();
44208         try {
44209             nodeRange.selectNode(node);
44210         } catch (e) {
44211             nodeRange.selectNodeContents(node);
44212         }
44213     
44214         var rangeStartRange = range.cloneRange();
44215         rangeStartRange.collapse(true);
44216     
44217         var rangeEndRange = range.cloneRange();
44218         rangeEndRange.collapse(false);
44219     
44220         var nodeStartRange = nodeRange.cloneRange();
44221         nodeStartRange.collapse(true);
44222     
44223         var nodeEndRange = nodeRange.cloneRange();
44224         nodeEndRange.collapse(false);
44225     
44226         return rangeStartRange.compareBoundaryPoints(
44227                  Range.START_TO_START, nodeEndRange) == -1 &&
44228                rangeEndRange.compareBoundaryPoints(
44229                  Range.START_TO_START, nodeStartRange) == 1;
44230         
44231          
44232     },
44233     rangeCompareNode : function(range, node)
44234     {
44235         var nodeRange = node.ownerDocument.createRange();
44236         try {
44237             nodeRange.selectNode(node);
44238         } catch (e) {
44239             nodeRange.selectNodeContents(node);
44240         }
44241         
44242         
44243         range.collapse(true);
44244     
44245         nodeRange.collapse(true);
44246      
44247         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44248         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44249          
44250         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44251         
44252         var nodeIsBefore   =  ss == 1;
44253         var nodeIsAfter    = ee == -1;
44254         
44255         if (nodeIsBefore && nodeIsAfter) {
44256             return 0; // outer
44257         }
44258         if (!nodeIsBefore && nodeIsAfter) {
44259             return 1; //right trailed.
44260         }
44261         
44262         if (nodeIsBefore && !nodeIsAfter) {
44263             return 2;  // left trailed.
44264         }
44265         // fully contined.
44266         return 3;
44267     },
44268
44269     // private? - in a new class?
44270     cleanUpPaste :  function()
44271     {
44272         // cleans up the whole document..
44273         Roo.log('cleanuppaste');
44274         
44275         this.cleanUpChildren(this.doc.body);
44276         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44277         if (clean != this.doc.body.innerHTML) {
44278             this.doc.body.innerHTML = clean;
44279         }
44280         
44281     },
44282     
44283     cleanWordChars : function(input) {// change the chars to hex code
44284         var he = Roo.HtmlEditorCore;
44285         
44286         var output = input;
44287         Roo.each(he.swapCodes, function(sw) { 
44288             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44289             
44290             output = output.replace(swapper, sw[1]);
44291         });
44292         
44293         return output;
44294     },
44295     
44296     
44297     cleanUpChildren : function (n)
44298     {
44299         if (!n.childNodes.length) {
44300             return;
44301         }
44302         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44303            this.cleanUpChild(n.childNodes[i]);
44304         }
44305     },
44306     
44307     
44308         
44309     
44310     cleanUpChild : function (node)
44311     {
44312         var ed = this;
44313         //console.log(node);
44314         if (node.nodeName == "#text") {
44315             // clean up silly Windows -- stuff?
44316             return; 
44317         }
44318         if (node.nodeName == "#comment") {
44319             node.parentNode.removeChild(node);
44320             // clean up silly Windows -- stuff?
44321             return; 
44322         }
44323         var lcname = node.tagName.toLowerCase();
44324         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44325         // whitelist of tags..
44326         
44327         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44328             // remove node.
44329             node.parentNode.removeChild(node);
44330             return;
44331             
44332         }
44333         
44334         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44335         
44336         // spans with no attributes - just remove them..
44337         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44338             remove_keep_children = true;
44339         }
44340         
44341         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44342         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44343         
44344         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44345         //    remove_keep_children = true;
44346         //}
44347         
44348         if (remove_keep_children) {
44349             this.cleanUpChildren(node);
44350             // inserts everything just before this node...
44351             while (node.childNodes.length) {
44352                 var cn = node.childNodes[0];
44353                 node.removeChild(cn);
44354                 node.parentNode.insertBefore(cn, node);
44355             }
44356             node.parentNode.removeChild(node);
44357             return;
44358         }
44359         
44360         if (!node.attributes || !node.attributes.length) {
44361             
44362           
44363             
44364             
44365             this.cleanUpChildren(node);
44366             return;
44367         }
44368         
44369         function cleanAttr(n,v)
44370         {
44371             
44372             if (v.match(/^\./) || v.match(/^\//)) {
44373                 return;
44374             }
44375             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44376                 return;
44377             }
44378             if (v.match(/^#/)) {
44379                 return;
44380             }
44381 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44382             node.removeAttribute(n);
44383             
44384         }
44385         
44386         var cwhite = this.cwhite;
44387         var cblack = this.cblack;
44388             
44389         function cleanStyle(n,v)
44390         {
44391             if (v.match(/expression/)) { //XSS?? should we even bother..
44392                 node.removeAttribute(n);
44393                 return;
44394             }
44395             
44396             var parts = v.split(/;/);
44397             var clean = [];
44398             
44399             Roo.each(parts, function(p) {
44400                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44401                 if (!p.length) {
44402                     return true;
44403                 }
44404                 var l = p.split(':').shift().replace(/\s+/g,'');
44405                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44406                 
44407                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44408 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44409                     //node.removeAttribute(n);
44410                     return true;
44411                 }
44412                 //Roo.log()
44413                 // only allow 'c whitelisted system attributes'
44414                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44415 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44416                     //node.removeAttribute(n);
44417                     return true;
44418                 }
44419                 
44420                 
44421                  
44422                 
44423                 clean.push(p);
44424                 return true;
44425             });
44426             if (clean.length) { 
44427                 node.setAttribute(n, clean.join(';'));
44428             } else {
44429                 node.removeAttribute(n);
44430             }
44431             
44432         }
44433         
44434         
44435         for (var i = node.attributes.length-1; i > -1 ; i--) {
44436             var a = node.attributes[i];
44437             //console.log(a);
44438             
44439             if (a.name.toLowerCase().substr(0,2)=='on')  {
44440                 node.removeAttribute(a.name);
44441                 continue;
44442             }
44443             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44444                 node.removeAttribute(a.name);
44445                 continue;
44446             }
44447             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44448                 cleanAttr(a.name,a.value); // fixme..
44449                 continue;
44450             }
44451             if (a.name == 'style') {
44452                 cleanStyle(a.name,a.value);
44453                 continue;
44454             }
44455             /// clean up MS crap..
44456             // tecnically this should be a list of valid class'es..
44457             
44458             
44459             if (a.name == 'class') {
44460                 if (a.value.match(/^Mso/)) {
44461                     node.removeAttribute('class');
44462                 }
44463                 
44464                 if (a.value.match(/^body$/)) {
44465                     node.removeAttribute('class');
44466                 }
44467                 continue;
44468             }
44469             
44470             // style cleanup!?
44471             // class cleanup?
44472             
44473         }
44474         
44475         
44476         this.cleanUpChildren(node);
44477         
44478         
44479     },
44480     
44481     /**
44482      * Clean up MS wordisms...
44483      */
44484     cleanWord : function(node)
44485     {
44486         if (!node) {
44487             this.cleanWord(this.doc.body);
44488             return;
44489         }
44490         
44491         if(
44492                 node.nodeName == 'SPAN' &&
44493                 !node.hasAttributes() &&
44494                 node.childNodes.length == 1 &&
44495                 node.firstChild.nodeName == "#text"  
44496         ) {
44497             var textNode = node.firstChild;
44498             node.removeChild(textNode);
44499             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44500                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44501             }
44502             node.parentNode.insertBefore(textNode, node);
44503             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44504                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44505             }
44506             node.parentNode.removeChild(node);
44507         }
44508         
44509         if (node.nodeName == "#text") {
44510             // clean up silly Windows -- stuff?
44511             return; 
44512         }
44513         if (node.nodeName == "#comment") {
44514             node.parentNode.removeChild(node);
44515             // clean up silly Windows -- stuff?
44516             return; 
44517         }
44518         
44519         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44520             node.parentNode.removeChild(node);
44521             return;
44522         }
44523         //Roo.log(node.tagName);
44524         // remove - but keep children..
44525         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44526             //Roo.log('-- removed');
44527             while (node.childNodes.length) {
44528                 var cn = node.childNodes[0];
44529                 node.removeChild(cn);
44530                 node.parentNode.insertBefore(cn, node);
44531                 // move node to parent - and clean it..
44532                 this.cleanWord(cn);
44533             }
44534             node.parentNode.removeChild(node);
44535             /// no need to iterate chidlren = it's got none..
44536             //this.iterateChildren(node, this.cleanWord);
44537             return;
44538         }
44539         // clean styles
44540         if (node.className.length) {
44541             
44542             var cn = node.className.split(/\W+/);
44543             var cna = [];
44544             Roo.each(cn, function(cls) {
44545                 if (cls.match(/Mso[a-zA-Z]+/)) {
44546                     return;
44547                 }
44548                 cna.push(cls);
44549             });
44550             node.className = cna.length ? cna.join(' ') : '';
44551             if (!cna.length) {
44552                 node.removeAttribute("class");
44553             }
44554         }
44555         
44556         if (node.hasAttribute("lang")) {
44557             node.removeAttribute("lang");
44558         }
44559         
44560         if (node.hasAttribute("style")) {
44561             
44562             var styles = node.getAttribute("style").split(";");
44563             var nstyle = [];
44564             Roo.each(styles, function(s) {
44565                 if (!s.match(/:/)) {
44566                     return;
44567                 }
44568                 var kv = s.split(":");
44569                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44570                     return;
44571                 }
44572                 // what ever is left... we allow.
44573                 nstyle.push(s);
44574             });
44575             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44576             if (!nstyle.length) {
44577                 node.removeAttribute('style');
44578             }
44579         }
44580         this.iterateChildren(node, this.cleanWord);
44581         
44582         
44583         
44584     },
44585     /**
44586      * iterateChildren of a Node, calling fn each time, using this as the scole..
44587      * @param {DomNode} node node to iterate children of.
44588      * @param {Function} fn method of this class to call on each item.
44589      */
44590     iterateChildren : function(node, fn)
44591     {
44592         if (!node.childNodes.length) {
44593                 return;
44594         }
44595         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44596            fn.call(this, node.childNodes[i])
44597         }
44598     },
44599     
44600     
44601     /**
44602      * cleanTableWidths.
44603      *
44604      * Quite often pasting from word etc.. results in tables with column and widths.
44605      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44606      *
44607      */
44608     cleanTableWidths : function(node)
44609     {
44610          
44611          
44612         if (!node) {
44613             this.cleanTableWidths(this.doc.body);
44614             return;
44615         }
44616         
44617         // ignore list...
44618         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44619             return; 
44620         }
44621         Roo.log(node.tagName);
44622         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44623             this.iterateChildren(node, this.cleanTableWidths);
44624             return;
44625         }
44626         if (node.hasAttribute('width')) {
44627             node.removeAttribute('width');
44628         }
44629         
44630          
44631         if (node.hasAttribute("style")) {
44632             // pretty basic...
44633             
44634             var styles = node.getAttribute("style").split(";");
44635             var nstyle = [];
44636             Roo.each(styles, function(s) {
44637                 if (!s.match(/:/)) {
44638                     return;
44639                 }
44640                 var kv = s.split(":");
44641                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44642                     return;
44643                 }
44644                 // what ever is left... we allow.
44645                 nstyle.push(s);
44646             });
44647             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44648             if (!nstyle.length) {
44649                 node.removeAttribute('style');
44650             }
44651         }
44652         
44653         this.iterateChildren(node, this.cleanTableWidths);
44654         
44655         
44656     },
44657     
44658     
44659     
44660     
44661     domToHTML : function(currentElement, depth, nopadtext) {
44662         
44663         depth = depth || 0;
44664         nopadtext = nopadtext || false;
44665     
44666         if (!currentElement) {
44667             return this.domToHTML(this.doc.body);
44668         }
44669         
44670         //Roo.log(currentElement);
44671         var j;
44672         var allText = false;
44673         var nodeName = currentElement.nodeName;
44674         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44675         
44676         if  (nodeName == '#text') {
44677             
44678             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44679         }
44680         
44681         
44682         var ret = '';
44683         if (nodeName != 'BODY') {
44684              
44685             var i = 0;
44686             // Prints the node tagName, such as <A>, <IMG>, etc
44687             if (tagName) {
44688                 var attr = [];
44689                 for(i = 0; i < currentElement.attributes.length;i++) {
44690                     // quoting?
44691                     var aname = currentElement.attributes.item(i).name;
44692                     if (!currentElement.attributes.item(i).value.length) {
44693                         continue;
44694                     }
44695                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44696                 }
44697                 
44698                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44699             } 
44700             else {
44701                 
44702                 // eack
44703             }
44704         } else {
44705             tagName = false;
44706         }
44707         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44708             return ret;
44709         }
44710         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44711             nopadtext = true;
44712         }
44713         
44714         
44715         // Traverse the tree
44716         i = 0;
44717         var currentElementChild = currentElement.childNodes.item(i);
44718         var allText = true;
44719         var innerHTML  = '';
44720         lastnode = '';
44721         while (currentElementChild) {
44722             // Formatting code (indent the tree so it looks nice on the screen)
44723             var nopad = nopadtext;
44724             if (lastnode == 'SPAN') {
44725                 nopad  = true;
44726             }
44727             // text
44728             if  (currentElementChild.nodeName == '#text') {
44729                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44730                 toadd = nopadtext ? toadd : toadd.trim();
44731                 if (!nopad && toadd.length > 80) {
44732                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44733                 }
44734                 innerHTML  += toadd;
44735                 
44736                 i++;
44737                 currentElementChild = currentElement.childNodes.item(i);
44738                 lastNode = '';
44739                 continue;
44740             }
44741             allText = false;
44742             
44743             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44744                 
44745             // Recursively traverse the tree structure of the child node
44746             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44747             lastnode = currentElementChild.nodeName;
44748             i++;
44749             currentElementChild=currentElement.childNodes.item(i);
44750         }
44751         
44752         ret += innerHTML;
44753         
44754         if (!allText) {
44755                 // The remaining code is mostly for formatting the tree
44756             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44757         }
44758         
44759         
44760         if (tagName) {
44761             ret+= "</"+tagName+">";
44762         }
44763         return ret;
44764         
44765     },
44766         
44767     applyBlacklists : function()
44768     {
44769         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44770         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44771         
44772         this.white = [];
44773         this.black = [];
44774         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44775             if (b.indexOf(tag) > -1) {
44776                 return;
44777             }
44778             this.white.push(tag);
44779             
44780         }, this);
44781         
44782         Roo.each(w, function(tag) {
44783             if (b.indexOf(tag) > -1) {
44784                 return;
44785             }
44786             if (this.white.indexOf(tag) > -1) {
44787                 return;
44788             }
44789             this.white.push(tag);
44790             
44791         }, this);
44792         
44793         
44794         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44795             if (w.indexOf(tag) > -1) {
44796                 return;
44797             }
44798             this.black.push(tag);
44799             
44800         }, this);
44801         
44802         Roo.each(b, function(tag) {
44803             if (w.indexOf(tag) > -1) {
44804                 return;
44805             }
44806             if (this.black.indexOf(tag) > -1) {
44807                 return;
44808             }
44809             this.black.push(tag);
44810             
44811         }, this);
44812         
44813         
44814         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44815         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44816         
44817         this.cwhite = [];
44818         this.cblack = [];
44819         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44820             if (b.indexOf(tag) > -1) {
44821                 return;
44822             }
44823             this.cwhite.push(tag);
44824             
44825         }, this);
44826         
44827         Roo.each(w, function(tag) {
44828             if (b.indexOf(tag) > -1) {
44829                 return;
44830             }
44831             if (this.cwhite.indexOf(tag) > -1) {
44832                 return;
44833             }
44834             this.cwhite.push(tag);
44835             
44836         }, this);
44837         
44838         
44839         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44840             if (w.indexOf(tag) > -1) {
44841                 return;
44842             }
44843             this.cblack.push(tag);
44844             
44845         }, this);
44846         
44847         Roo.each(b, function(tag) {
44848             if (w.indexOf(tag) > -1) {
44849                 return;
44850             }
44851             if (this.cblack.indexOf(tag) > -1) {
44852                 return;
44853             }
44854             this.cblack.push(tag);
44855             
44856         }, this);
44857     },
44858     
44859     setStylesheets : function(stylesheets)
44860     {
44861         if(typeof(stylesheets) == 'string'){
44862             Roo.get(this.iframe.contentDocument.head).createChild({
44863                 tag : 'link',
44864                 rel : 'stylesheet',
44865                 type : 'text/css',
44866                 href : stylesheets
44867             });
44868             
44869             return;
44870         }
44871         var _this = this;
44872      
44873         Roo.each(stylesheets, function(s) {
44874             if(!s.length){
44875                 return;
44876             }
44877             
44878             Roo.get(_this.iframe.contentDocument.head).createChild({
44879                 tag : 'link',
44880                 rel : 'stylesheet',
44881                 type : 'text/css',
44882                 href : s
44883             });
44884         });
44885
44886         
44887     },
44888     
44889     removeStylesheets : function()
44890     {
44891         var _this = this;
44892         
44893         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44894             s.remove();
44895         });
44896     },
44897     
44898     setStyle : function(style)
44899     {
44900         Roo.get(this.iframe.contentDocument.head).createChild({
44901             tag : 'style',
44902             type : 'text/css',
44903             html : style
44904         });
44905
44906         return;
44907     }
44908     
44909     // hide stuff that is not compatible
44910     /**
44911      * @event blur
44912      * @hide
44913      */
44914     /**
44915      * @event change
44916      * @hide
44917      */
44918     /**
44919      * @event focus
44920      * @hide
44921      */
44922     /**
44923      * @event specialkey
44924      * @hide
44925      */
44926     /**
44927      * @cfg {String} fieldClass @hide
44928      */
44929     /**
44930      * @cfg {String} focusClass @hide
44931      */
44932     /**
44933      * @cfg {String} autoCreate @hide
44934      */
44935     /**
44936      * @cfg {String} inputType @hide
44937      */
44938     /**
44939      * @cfg {String} invalidClass @hide
44940      */
44941     /**
44942      * @cfg {String} invalidText @hide
44943      */
44944     /**
44945      * @cfg {String} msgFx @hide
44946      */
44947     /**
44948      * @cfg {String} validateOnBlur @hide
44949      */
44950 });
44951
44952 Roo.HtmlEditorCore.white = [
44953         'area', 'br', 'img', 'input', 'hr', 'wbr',
44954         
44955        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44956        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44957        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44958        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44959        'table',   'ul',         'xmp', 
44960        
44961        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44962       'thead',   'tr', 
44963      
44964       'dir', 'menu', 'ol', 'ul', 'dl',
44965        
44966       'embed',  'object'
44967 ];
44968
44969
44970 Roo.HtmlEditorCore.black = [
44971     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44972         'applet', // 
44973         'base',   'basefont', 'bgsound', 'blink',  'body', 
44974         'frame',  'frameset', 'head',    'html',   'ilayer', 
44975         'iframe', 'layer',  'link',     'meta',    'object',   
44976         'script', 'style' ,'title',  'xml' // clean later..
44977 ];
44978 Roo.HtmlEditorCore.clean = [
44979     'script', 'style', 'title', 'xml'
44980 ];
44981 Roo.HtmlEditorCore.remove = [
44982     'font'
44983 ];
44984 // attributes..
44985
44986 Roo.HtmlEditorCore.ablack = [
44987     'on'
44988 ];
44989     
44990 Roo.HtmlEditorCore.aclean = [ 
44991     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44992 ];
44993
44994 // protocols..
44995 Roo.HtmlEditorCore.pwhite= [
44996         'http',  'https',  'mailto'
44997 ];
44998
44999 // white listed style attributes.
45000 Roo.HtmlEditorCore.cwhite= [
45001       //  'text-align', /// default is to allow most things..
45002       
45003          
45004 //        'font-size'//??
45005 ];
45006
45007 // black listed style attributes.
45008 Roo.HtmlEditorCore.cblack= [
45009       //  'font-size' -- this can be set by the project 
45010 ];
45011
45012
45013 Roo.HtmlEditorCore.swapCodes   =[ 
45014     [    8211, "--" ], 
45015     [    8212, "--" ], 
45016     [    8216,  "'" ],  
45017     [    8217, "'" ],  
45018     [    8220, '"' ],  
45019     [    8221, '"' ],  
45020     [    8226, "*" ],  
45021     [    8230, "..." ]
45022 ]; 
45023
45024     //<script type="text/javascript">
45025
45026 /*
45027  * Ext JS Library 1.1.1
45028  * Copyright(c) 2006-2007, Ext JS, LLC.
45029  * Licence LGPL
45030  * 
45031  */
45032  
45033  
45034 Roo.form.HtmlEditor = function(config){
45035     
45036     
45037     
45038     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45039     
45040     if (!this.toolbars) {
45041         this.toolbars = [];
45042     }
45043     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45044     
45045     
45046 };
45047
45048 /**
45049  * @class Roo.form.HtmlEditor
45050  * @extends Roo.form.Field
45051  * Provides a lightweight HTML Editor component.
45052  *
45053  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45054  * 
45055  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45056  * supported by this editor.</b><br/><br/>
45057  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45058  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45059  */
45060 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45061     /**
45062      * @cfg {Boolean} clearUp
45063      */
45064     clearUp : true,
45065       /**
45066      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45067      */
45068     toolbars : false,
45069    
45070      /**
45071      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45072      *                        Roo.resizable.
45073      */
45074     resizable : false,
45075      /**
45076      * @cfg {Number} height (in pixels)
45077      */   
45078     height: 300,
45079    /**
45080      * @cfg {Number} width (in pixels)
45081      */   
45082     width: 500,
45083     
45084     /**
45085      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45086      * 
45087      */
45088     stylesheets: false,
45089     
45090     
45091      /**
45092      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45093      * 
45094      */
45095     cblack: false,
45096     /**
45097      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45098      * 
45099      */
45100     cwhite: false,
45101     
45102      /**
45103      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45104      * 
45105      */
45106     black: false,
45107     /**
45108      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45109      * 
45110      */
45111     white: false,
45112     
45113     // id of frame..
45114     frameId: false,
45115     
45116     // private properties
45117     validationEvent : false,
45118     deferHeight: true,
45119     initialized : false,
45120     activated : false,
45121     
45122     onFocus : Roo.emptyFn,
45123     iframePad:3,
45124     hideMode:'offsets',
45125     
45126     actionMode : 'container', // defaults to hiding it...
45127     
45128     defaultAutoCreate : { // modified by initCompnoent..
45129         tag: "textarea",
45130         style:"width:500px;height:300px;",
45131         autocomplete: "new-password"
45132     },
45133
45134     // private
45135     initComponent : function(){
45136         this.addEvents({
45137             /**
45138              * @event initialize
45139              * Fires when the editor is fully initialized (including the iframe)
45140              * @param {HtmlEditor} this
45141              */
45142             initialize: true,
45143             /**
45144              * @event activate
45145              * Fires when the editor is first receives the focus. Any insertion must wait
45146              * until after this event.
45147              * @param {HtmlEditor} this
45148              */
45149             activate: true,
45150              /**
45151              * @event beforesync
45152              * Fires before the textarea is updated with content from the editor iframe. Return false
45153              * to cancel the sync.
45154              * @param {HtmlEditor} this
45155              * @param {String} html
45156              */
45157             beforesync: true,
45158              /**
45159              * @event beforepush
45160              * Fires before the iframe editor is updated with content from the textarea. Return false
45161              * to cancel the push.
45162              * @param {HtmlEditor} this
45163              * @param {String} html
45164              */
45165             beforepush: true,
45166              /**
45167              * @event sync
45168              * Fires when the textarea is updated with content from the editor iframe.
45169              * @param {HtmlEditor} this
45170              * @param {String} html
45171              */
45172             sync: true,
45173              /**
45174              * @event push
45175              * Fires when the iframe editor is updated with content from the textarea.
45176              * @param {HtmlEditor} this
45177              * @param {String} html
45178              */
45179             push: true,
45180              /**
45181              * @event editmodechange
45182              * Fires when the editor switches edit modes
45183              * @param {HtmlEditor} this
45184              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45185              */
45186             editmodechange: true,
45187             /**
45188              * @event editorevent
45189              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45190              * @param {HtmlEditor} this
45191              */
45192             editorevent: true,
45193             /**
45194              * @event firstfocus
45195              * Fires when on first focus - needed by toolbars..
45196              * @param {HtmlEditor} this
45197              */
45198             firstfocus: true,
45199             /**
45200              * @event autosave
45201              * Auto save the htmlEditor value as a file into Events
45202              * @param {HtmlEditor} this
45203              */
45204             autosave: true,
45205             /**
45206              * @event savedpreview
45207              * preview the saved version of htmlEditor
45208              * @param {HtmlEditor} this
45209              */
45210             savedpreview: true,
45211             
45212             /**
45213             * @event stylesheetsclick
45214             * Fires when press the Sytlesheets button
45215             * @param {Roo.HtmlEditorCore} this
45216             */
45217             stylesheetsclick: true
45218         });
45219         this.defaultAutoCreate =  {
45220             tag: "textarea",
45221             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45222             autocomplete: "new-password"
45223         };
45224     },
45225
45226     /**
45227      * Protected method that will not generally be called directly. It
45228      * is called when the editor creates its toolbar. Override this method if you need to
45229      * add custom toolbar buttons.
45230      * @param {HtmlEditor} editor
45231      */
45232     createToolbar : function(editor){
45233         Roo.log("create toolbars");
45234         if (!editor.toolbars || !editor.toolbars.length) {
45235             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45236         }
45237         
45238         for (var i =0 ; i < editor.toolbars.length;i++) {
45239             editor.toolbars[i] = Roo.factory(
45240                     typeof(editor.toolbars[i]) == 'string' ?
45241                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45242                 Roo.form.HtmlEditor);
45243             editor.toolbars[i].init(editor);
45244         }
45245          
45246         
45247     },
45248
45249      
45250     // private
45251     onRender : function(ct, position)
45252     {
45253         var _t = this;
45254         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45255         
45256         this.wrap = this.el.wrap({
45257             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45258         });
45259         
45260         this.editorcore.onRender(ct, position);
45261          
45262         if (this.resizable) {
45263             this.resizeEl = new Roo.Resizable(this.wrap, {
45264                 pinned : true,
45265                 wrap: true,
45266                 dynamic : true,
45267                 minHeight : this.height,
45268                 height: this.height,
45269                 handles : this.resizable,
45270                 width: this.width,
45271                 listeners : {
45272                     resize : function(r, w, h) {
45273                         _t.onResize(w,h); // -something
45274                     }
45275                 }
45276             });
45277             
45278         }
45279         this.createToolbar(this);
45280        
45281         
45282         if(!this.width){
45283             this.setSize(this.wrap.getSize());
45284         }
45285         if (this.resizeEl) {
45286             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45287             // should trigger onReize..
45288         }
45289         
45290         this.keyNav = new Roo.KeyNav(this.el, {
45291             
45292             "tab" : function(e){
45293                 e.preventDefault();
45294                 
45295                 var value = this.getValue();
45296                 
45297                 var start = this.el.dom.selectionStart;
45298                 var end = this.el.dom.selectionEnd;
45299                 
45300                 if(!e.shiftKey){
45301                     
45302                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45303                     this.el.dom.setSelectionRange(end + 1, end + 1);
45304                     return;
45305                 }
45306                 
45307                 var f = value.substring(0, start).split("\t");
45308                 
45309                 if(f.pop().length != 0){
45310                     return;
45311                 }
45312                 
45313                 this.setValue(f.join("\t") + value.substring(end));
45314                 this.el.dom.setSelectionRange(start - 1, start - 1);
45315                 
45316             },
45317             
45318             "home" : function(e){
45319                 e.preventDefault();
45320                 
45321                 var curr = this.el.dom.selectionStart;
45322                 var lines = this.getValue().split("\n");
45323                 
45324                 if(!lines.length){
45325                     return;
45326                 }
45327                 
45328                 if(e.ctrlKey){
45329                     this.el.dom.setSelectionRange(0, 0);
45330                     return;
45331                 }
45332                 
45333                 var pos = 0;
45334                 
45335                 for (var i = 0; i < lines.length;i++) {
45336                     pos += lines[i].length;
45337                     
45338                     if(i != 0){
45339                         pos += 1;
45340                     }
45341                     
45342                     if(pos < curr){
45343                         continue;
45344                     }
45345                     
45346                     pos -= lines[i].length;
45347                     
45348                     break;
45349                 }
45350                 
45351                 if(!e.shiftKey){
45352                     this.el.dom.setSelectionRange(pos, pos);
45353                     return;
45354                 }
45355                 
45356                 this.el.dom.selectionStart = pos;
45357                 this.el.dom.selectionEnd = curr;
45358             },
45359             
45360             "end" : function(e){
45361                 e.preventDefault();
45362                 
45363                 var curr = this.el.dom.selectionStart;
45364                 var lines = this.getValue().split("\n");
45365                 
45366                 if(!lines.length){
45367                     return;
45368                 }
45369                 
45370                 if(e.ctrlKey){
45371                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45372                     return;
45373                 }
45374                 
45375                 var pos = 0;
45376                 
45377                 for (var i = 0; i < lines.length;i++) {
45378                     
45379                     pos += lines[i].length;
45380                     
45381                     if(i != 0){
45382                         pos += 1;
45383                     }
45384                     
45385                     if(pos < curr){
45386                         continue;
45387                     }
45388                     
45389                     break;
45390                 }
45391                 
45392                 if(!e.shiftKey){
45393                     this.el.dom.setSelectionRange(pos, pos);
45394                     return;
45395                 }
45396                 
45397                 this.el.dom.selectionStart = curr;
45398                 this.el.dom.selectionEnd = pos;
45399             },
45400
45401             scope : this,
45402
45403             doRelay : function(foo, bar, hname){
45404                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45405             },
45406
45407             forceKeyDown: true
45408         });
45409         
45410 //        if(this.autosave && this.w){
45411 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45412 //        }
45413     },
45414
45415     // private
45416     onResize : function(w, h)
45417     {
45418         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45419         var ew = false;
45420         var eh = false;
45421         
45422         if(this.el ){
45423             if(typeof w == 'number'){
45424                 var aw = w - this.wrap.getFrameWidth('lr');
45425                 this.el.setWidth(this.adjustWidth('textarea', aw));
45426                 ew = aw;
45427             }
45428             if(typeof h == 'number'){
45429                 var tbh = 0;
45430                 for (var i =0; i < this.toolbars.length;i++) {
45431                     // fixme - ask toolbars for heights?
45432                     tbh += this.toolbars[i].tb.el.getHeight();
45433                     if (this.toolbars[i].footer) {
45434                         tbh += this.toolbars[i].footer.el.getHeight();
45435                     }
45436                 }
45437                 
45438                 
45439                 
45440                 
45441                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45442                 ah -= 5; // knock a few pixes off for look..
45443 //                Roo.log(ah);
45444                 this.el.setHeight(this.adjustWidth('textarea', ah));
45445                 var eh = ah;
45446             }
45447         }
45448         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45449         this.editorcore.onResize(ew,eh);
45450         
45451     },
45452
45453     /**
45454      * Toggles the editor between standard and source edit mode.
45455      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45456      */
45457     toggleSourceEdit : function(sourceEditMode)
45458     {
45459         this.editorcore.toggleSourceEdit(sourceEditMode);
45460         
45461         if(this.editorcore.sourceEditMode){
45462             Roo.log('editor - showing textarea');
45463             
45464 //            Roo.log('in');
45465 //            Roo.log(this.syncValue());
45466             this.editorcore.syncValue();
45467             this.el.removeClass('x-hidden');
45468             this.el.dom.removeAttribute('tabIndex');
45469             this.el.focus();
45470             
45471             for (var i = 0; i < this.toolbars.length; i++) {
45472                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45473                     this.toolbars[i].tb.hide();
45474                     this.toolbars[i].footer.hide();
45475                 }
45476             }
45477             
45478         }else{
45479             Roo.log('editor - hiding textarea');
45480 //            Roo.log('out')
45481 //            Roo.log(this.pushValue()); 
45482             this.editorcore.pushValue();
45483             
45484             this.el.addClass('x-hidden');
45485             this.el.dom.setAttribute('tabIndex', -1);
45486             
45487             for (var i = 0; i < this.toolbars.length; i++) {
45488                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45489                     this.toolbars[i].tb.show();
45490                     this.toolbars[i].footer.show();
45491                 }
45492             }
45493             
45494             //this.deferFocus();
45495         }
45496         
45497         this.setSize(this.wrap.getSize());
45498         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45499         
45500         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45501     },
45502  
45503     // private (for BoxComponent)
45504     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45505
45506     // private (for BoxComponent)
45507     getResizeEl : function(){
45508         return this.wrap;
45509     },
45510
45511     // private (for BoxComponent)
45512     getPositionEl : function(){
45513         return this.wrap;
45514     },
45515
45516     // private
45517     initEvents : function(){
45518         this.originalValue = this.getValue();
45519     },
45520
45521     /**
45522      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45523      * @method
45524      */
45525     markInvalid : Roo.emptyFn,
45526     /**
45527      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45528      * @method
45529      */
45530     clearInvalid : Roo.emptyFn,
45531
45532     setValue : function(v){
45533         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45534         this.editorcore.pushValue();
45535     },
45536
45537      
45538     // private
45539     deferFocus : function(){
45540         this.focus.defer(10, this);
45541     },
45542
45543     // doc'ed in Field
45544     focus : function(){
45545         this.editorcore.focus();
45546         
45547     },
45548       
45549
45550     // private
45551     onDestroy : function(){
45552         
45553         
45554         
45555         if(this.rendered){
45556             
45557             for (var i =0; i < this.toolbars.length;i++) {
45558                 // fixme - ask toolbars for heights?
45559                 this.toolbars[i].onDestroy();
45560             }
45561             
45562             this.wrap.dom.innerHTML = '';
45563             this.wrap.remove();
45564         }
45565     },
45566
45567     // private
45568     onFirstFocus : function(){
45569         //Roo.log("onFirstFocus");
45570         this.editorcore.onFirstFocus();
45571          for (var i =0; i < this.toolbars.length;i++) {
45572             this.toolbars[i].onFirstFocus();
45573         }
45574         
45575     },
45576     
45577     // private
45578     syncValue : function()
45579     {
45580         this.editorcore.syncValue();
45581     },
45582     
45583     pushValue : function()
45584     {
45585         this.editorcore.pushValue();
45586     },
45587     
45588     setStylesheets : function(stylesheets)
45589     {
45590         this.editorcore.setStylesheets(stylesheets);
45591     },
45592     
45593     removeStylesheets : function()
45594     {
45595         this.editorcore.removeStylesheets();
45596     }
45597      
45598     
45599     // hide stuff that is not compatible
45600     /**
45601      * @event blur
45602      * @hide
45603      */
45604     /**
45605      * @event change
45606      * @hide
45607      */
45608     /**
45609      * @event focus
45610      * @hide
45611      */
45612     /**
45613      * @event specialkey
45614      * @hide
45615      */
45616     /**
45617      * @cfg {String} fieldClass @hide
45618      */
45619     /**
45620      * @cfg {String} focusClass @hide
45621      */
45622     /**
45623      * @cfg {String} autoCreate @hide
45624      */
45625     /**
45626      * @cfg {String} inputType @hide
45627      */
45628     /**
45629      * @cfg {String} invalidClass @hide
45630      */
45631     /**
45632      * @cfg {String} invalidText @hide
45633      */
45634     /**
45635      * @cfg {String} msgFx @hide
45636      */
45637     /**
45638      * @cfg {String} validateOnBlur @hide
45639      */
45640 });
45641  
45642     // <script type="text/javascript">
45643 /*
45644  * Based on
45645  * Ext JS Library 1.1.1
45646  * Copyright(c) 2006-2007, Ext JS, LLC.
45647  *  
45648  
45649  */
45650
45651 /**
45652  * @class Roo.form.HtmlEditorToolbar1
45653  * Basic Toolbar
45654  * 
45655  * Usage:
45656  *
45657  new Roo.form.HtmlEditor({
45658     ....
45659     toolbars : [
45660         new Roo.form.HtmlEditorToolbar1({
45661             disable : { fonts: 1 , format: 1, ..., ... , ...],
45662             btns : [ .... ]
45663         })
45664     }
45665      
45666  * 
45667  * @cfg {Object} disable List of elements to disable..
45668  * @cfg {Array} btns List of additional buttons.
45669  * 
45670  * 
45671  * NEEDS Extra CSS? 
45672  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45673  */
45674  
45675 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45676 {
45677     
45678     Roo.apply(this, config);
45679     
45680     // default disabled, based on 'good practice'..
45681     this.disable = this.disable || {};
45682     Roo.applyIf(this.disable, {
45683         fontSize : true,
45684         colors : true,
45685         specialElements : true
45686     });
45687     
45688     
45689     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45690     // dont call parent... till later.
45691 }
45692
45693 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45694     
45695     tb: false,
45696     
45697     rendered: false,
45698     
45699     editor : false,
45700     editorcore : false,
45701     /**
45702      * @cfg {Object} disable  List of toolbar elements to disable
45703          
45704      */
45705     disable : false,
45706     
45707     
45708      /**
45709      * @cfg {String} createLinkText The default text for the create link prompt
45710      */
45711     createLinkText : 'Please enter the URL for the link:',
45712     /**
45713      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45714      */
45715     defaultLinkValue : 'http:/'+'/',
45716    
45717     
45718       /**
45719      * @cfg {Array} fontFamilies An array of available font families
45720      */
45721     fontFamilies : [
45722         'Arial',
45723         'Courier New',
45724         'Tahoma',
45725         'Times New Roman',
45726         'Verdana'
45727     ],
45728     
45729     specialChars : [
45730            "&#169;",
45731           "&#174;",     
45732           "&#8482;",    
45733           "&#163;" ,    
45734          // "&#8212;",    
45735           "&#8230;",    
45736           "&#247;" ,    
45737         //  "&#225;" ,     ?? a acute?
45738            "&#8364;"    , //Euro
45739        //   "&#8220;"    ,
45740         //  "&#8221;"    ,
45741         //  "&#8226;"    ,
45742           "&#176;"  //   , // degrees
45743
45744          // "&#233;"     , // e ecute
45745          // "&#250;"     , // u ecute?
45746     ],
45747     
45748     specialElements : [
45749         {
45750             text: "Insert Table",
45751             xtype: 'MenuItem',
45752             xns : Roo.Menu,
45753             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45754                 
45755         },
45756         {    
45757             text: "Insert Image",
45758             xtype: 'MenuItem',
45759             xns : Roo.Menu,
45760             ihtml : '<img src="about:blank"/>'
45761             
45762         }
45763         
45764          
45765     ],
45766     
45767     
45768     inputElements : [ 
45769             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45770             "input:submit", "input:button", "select", "textarea", "label" ],
45771     formats : [
45772         ["p"] ,  
45773         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45774         ["pre"],[ "code"], 
45775         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45776         ['div'],['span'],
45777         ['sup'],['sub']
45778     ],
45779     
45780     cleanStyles : [
45781         "font-size"
45782     ],
45783      /**
45784      * @cfg {String} defaultFont default font to use.
45785      */
45786     defaultFont: 'tahoma',
45787    
45788     fontSelect : false,
45789     
45790     
45791     formatCombo : false,
45792     
45793     init : function(editor)
45794     {
45795         this.editor = editor;
45796         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45797         var editorcore = this.editorcore;
45798         
45799         var _t = this;
45800         
45801         var fid = editorcore.frameId;
45802         var etb = this;
45803         function btn(id, toggle, handler){
45804             var xid = fid + '-'+ id ;
45805             return {
45806                 id : xid,
45807                 cmd : id,
45808                 cls : 'x-btn-icon x-edit-'+id,
45809                 enableToggle:toggle !== false,
45810                 scope: _t, // was editor...
45811                 handler:handler||_t.relayBtnCmd,
45812                 clickEvent:'mousedown',
45813                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45814                 tabIndex:-1
45815             };
45816         }
45817         
45818         
45819         
45820         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45821         this.tb = tb;
45822          // stop form submits
45823         tb.el.on('click', function(e){
45824             e.preventDefault(); // what does this do?
45825         });
45826
45827         if(!this.disable.font) { // && !Roo.isSafari){
45828             /* why no safari for fonts 
45829             editor.fontSelect = tb.el.createChild({
45830                 tag:'select',
45831                 tabIndex: -1,
45832                 cls:'x-font-select',
45833                 html: this.createFontOptions()
45834             });
45835             
45836             editor.fontSelect.on('change', function(){
45837                 var font = editor.fontSelect.dom.value;
45838                 editor.relayCmd('fontname', font);
45839                 editor.deferFocus();
45840             }, editor);
45841             
45842             tb.add(
45843                 editor.fontSelect.dom,
45844                 '-'
45845             );
45846             */
45847             
45848         };
45849         if(!this.disable.formats){
45850             this.formatCombo = new Roo.form.ComboBox({
45851                 store: new Roo.data.SimpleStore({
45852                     id : 'tag',
45853                     fields: ['tag'],
45854                     data : this.formats // from states.js
45855                 }),
45856                 blockFocus : true,
45857                 name : '',
45858                 //autoCreate : {tag: "div",  size: "20"},
45859                 displayField:'tag',
45860                 typeAhead: false,
45861                 mode: 'local',
45862                 editable : false,
45863                 triggerAction: 'all',
45864                 emptyText:'Add tag',
45865                 selectOnFocus:true,
45866                 width:135,
45867                 listeners : {
45868                     'select': function(c, r, i) {
45869                         editorcore.insertTag(r.get('tag'));
45870                         editor.focus();
45871                     }
45872                 }
45873
45874             });
45875             tb.addField(this.formatCombo);
45876             
45877         }
45878         
45879         if(!this.disable.format){
45880             tb.add(
45881                 btn('bold'),
45882                 btn('italic'),
45883                 btn('underline'),
45884                 btn('strikethrough')
45885             );
45886         };
45887         if(!this.disable.fontSize){
45888             tb.add(
45889                 '-',
45890                 
45891                 
45892                 btn('increasefontsize', false, editorcore.adjustFont),
45893                 btn('decreasefontsize', false, editorcore.adjustFont)
45894             );
45895         };
45896         
45897         
45898         if(!this.disable.colors){
45899             tb.add(
45900                 '-', {
45901                     id:editorcore.frameId +'-forecolor',
45902                     cls:'x-btn-icon x-edit-forecolor',
45903                     clickEvent:'mousedown',
45904                     tooltip: this.buttonTips['forecolor'] || undefined,
45905                     tabIndex:-1,
45906                     menu : new Roo.menu.ColorMenu({
45907                         allowReselect: true,
45908                         focus: Roo.emptyFn,
45909                         value:'000000',
45910                         plain:true,
45911                         selectHandler: function(cp, color){
45912                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45913                             editor.deferFocus();
45914                         },
45915                         scope: editorcore,
45916                         clickEvent:'mousedown'
45917                     })
45918                 }, {
45919                     id:editorcore.frameId +'backcolor',
45920                     cls:'x-btn-icon x-edit-backcolor',
45921                     clickEvent:'mousedown',
45922                     tooltip: this.buttonTips['backcolor'] || undefined,
45923                     tabIndex:-1,
45924                     menu : new Roo.menu.ColorMenu({
45925                         focus: Roo.emptyFn,
45926                         value:'FFFFFF',
45927                         plain:true,
45928                         allowReselect: true,
45929                         selectHandler: function(cp, color){
45930                             if(Roo.isGecko){
45931                                 editorcore.execCmd('useCSS', false);
45932                                 editorcore.execCmd('hilitecolor', color);
45933                                 editorcore.execCmd('useCSS', true);
45934                                 editor.deferFocus();
45935                             }else{
45936                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45937                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45938                                 editor.deferFocus();
45939                             }
45940                         },
45941                         scope:editorcore,
45942                         clickEvent:'mousedown'
45943                     })
45944                 }
45945             );
45946         };
45947         // now add all the items...
45948         
45949
45950         if(!this.disable.alignments){
45951             tb.add(
45952                 '-',
45953                 btn('justifyleft'),
45954                 btn('justifycenter'),
45955                 btn('justifyright')
45956             );
45957         };
45958
45959         //if(!Roo.isSafari){
45960             if(!this.disable.links){
45961                 tb.add(
45962                     '-',
45963                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45964                 );
45965             };
45966
45967             if(!this.disable.lists){
45968                 tb.add(
45969                     '-',
45970                     btn('insertorderedlist'),
45971                     btn('insertunorderedlist')
45972                 );
45973             }
45974             if(!this.disable.sourceEdit){
45975                 tb.add(
45976                     '-',
45977                     btn('sourceedit', true, function(btn){
45978                         this.toggleSourceEdit(btn.pressed);
45979                     })
45980                 );
45981             }
45982         //}
45983         
45984         var smenu = { };
45985         // special menu.. - needs to be tidied up..
45986         if (!this.disable.special) {
45987             smenu = {
45988                 text: "&#169;",
45989                 cls: 'x-edit-none',
45990                 
45991                 menu : {
45992                     items : []
45993                 }
45994             };
45995             for (var i =0; i < this.specialChars.length; i++) {
45996                 smenu.menu.items.push({
45997                     
45998                     html: this.specialChars[i],
45999                     handler: function(a,b) {
46000                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46001                         //editor.insertAtCursor(a.html);
46002                         
46003                     },
46004                     tabIndex:-1
46005                 });
46006             }
46007             
46008             
46009             tb.add(smenu);
46010             
46011             
46012         }
46013         
46014         var cmenu = { };
46015         if (!this.disable.cleanStyles) {
46016             cmenu = {
46017                 cls: 'x-btn-icon x-btn-clear',
46018                 
46019                 menu : {
46020                     items : []
46021                 }
46022             };
46023             for (var i =0; i < this.cleanStyles.length; i++) {
46024                 cmenu.menu.items.push({
46025                     actiontype : this.cleanStyles[i],
46026                     html: 'Remove ' + this.cleanStyles[i],
46027                     handler: function(a,b) {
46028 //                        Roo.log(a);
46029 //                        Roo.log(b);
46030                         var c = Roo.get(editorcore.doc.body);
46031                         c.select('[style]').each(function(s) {
46032                             s.dom.style.removeProperty(a.actiontype);
46033                         });
46034                         editorcore.syncValue();
46035                     },
46036                     tabIndex:-1
46037                 });
46038             }
46039              cmenu.menu.items.push({
46040                 actiontype : 'tablewidths',
46041                 html: 'Remove Table Widths',
46042                 handler: function(a,b) {
46043                     editorcore.cleanTableWidths();
46044                     editorcore.syncValue();
46045                 },
46046                 tabIndex:-1
46047             });
46048             cmenu.menu.items.push({
46049                 actiontype : 'word',
46050                 html: 'Remove MS Word Formating',
46051                 handler: function(a,b) {
46052                     editorcore.cleanWord();
46053                     editorcore.syncValue();
46054                 },
46055                 tabIndex:-1
46056             });
46057             
46058             cmenu.menu.items.push({
46059                 actiontype : 'all',
46060                 html: 'Remove All Styles',
46061                 handler: function(a,b) {
46062                     
46063                     var c = Roo.get(editorcore.doc.body);
46064                     c.select('[style]').each(function(s) {
46065                         s.dom.removeAttribute('style');
46066                     });
46067                     editorcore.syncValue();
46068                 },
46069                 tabIndex:-1
46070             });
46071             
46072             cmenu.menu.items.push({
46073                 actiontype : 'all',
46074                 html: 'Remove All CSS Classes',
46075                 handler: function(a,b) {
46076                     
46077                     var c = Roo.get(editorcore.doc.body);
46078                     c.select('[class]').each(function(s) {
46079                         s.dom.removeAttribute('class');
46080                     });
46081                     editorcore.cleanWord();
46082                     editorcore.syncValue();
46083                 },
46084                 tabIndex:-1
46085             });
46086             
46087              cmenu.menu.items.push({
46088                 actiontype : 'tidy',
46089                 html: 'Tidy HTML Source',
46090                 handler: function(a,b) {
46091                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46092                     editorcore.syncValue();
46093                 },
46094                 tabIndex:-1
46095             });
46096             
46097             
46098             tb.add(cmenu);
46099         }
46100          
46101         if (!this.disable.specialElements) {
46102             var semenu = {
46103                 text: "Other;",
46104                 cls: 'x-edit-none',
46105                 menu : {
46106                     items : []
46107                 }
46108             };
46109             for (var i =0; i < this.specialElements.length; i++) {
46110                 semenu.menu.items.push(
46111                     Roo.apply({ 
46112                         handler: function(a,b) {
46113                             editor.insertAtCursor(this.ihtml);
46114                         }
46115                     }, this.specialElements[i])
46116                 );
46117                     
46118             }
46119             
46120             tb.add(semenu);
46121             
46122             
46123         }
46124          
46125         
46126         if (this.btns) {
46127             for(var i =0; i< this.btns.length;i++) {
46128                 var b = Roo.factory(this.btns[i],Roo.form);
46129                 b.cls =  'x-edit-none';
46130                 
46131                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46132                     b.cls += ' x-init-enable';
46133                 }
46134                 
46135                 b.scope = editorcore;
46136                 tb.add(b);
46137             }
46138         
46139         }
46140         
46141         
46142         
46143         // disable everything...
46144         
46145         this.tb.items.each(function(item){
46146             
46147            if(
46148                 item.id != editorcore.frameId+ '-sourceedit' && 
46149                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46150             ){
46151                 
46152                 item.disable();
46153             }
46154         });
46155         this.rendered = true;
46156         
46157         // the all the btns;
46158         editor.on('editorevent', this.updateToolbar, this);
46159         // other toolbars need to implement this..
46160         //editor.on('editmodechange', this.updateToolbar, this);
46161     },
46162     
46163     
46164     relayBtnCmd : function(btn) {
46165         this.editorcore.relayCmd(btn.cmd);
46166     },
46167     // private used internally
46168     createLink : function(){
46169         Roo.log("create link?");
46170         var url = prompt(this.createLinkText, this.defaultLinkValue);
46171         if(url && url != 'http:/'+'/'){
46172             this.editorcore.relayCmd('createlink', url);
46173         }
46174     },
46175
46176     
46177     /**
46178      * Protected method that will not generally be called directly. It triggers
46179      * a toolbar update by reading the markup state of the current selection in the editor.
46180      */
46181     updateToolbar: function(){
46182
46183         if(!this.editorcore.activated){
46184             this.editor.onFirstFocus();
46185             return;
46186         }
46187
46188         var btns = this.tb.items.map, 
46189             doc = this.editorcore.doc,
46190             frameId = this.editorcore.frameId;
46191
46192         if(!this.disable.font && !Roo.isSafari){
46193             /*
46194             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46195             if(name != this.fontSelect.dom.value){
46196                 this.fontSelect.dom.value = name;
46197             }
46198             */
46199         }
46200         if(!this.disable.format){
46201             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46202             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46203             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46204             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46205         }
46206         if(!this.disable.alignments){
46207             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46208             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46209             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46210         }
46211         if(!Roo.isSafari && !this.disable.lists){
46212             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46213             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46214         }
46215         
46216         var ans = this.editorcore.getAllAncestors();
46217         if (this.formatCombo) {
46218             
46219             
46220             var store = this.formatCombo.store;
46221             this.formatCombo.setValue("");
46222             for (var i =0; i < ans.length;i++) {
46223                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46224                     // select it..
46225                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46226                     break;
46227                 }
46228             }
46229         }
46230         
46231         
46232         
46233         // hides menus... - so this cant be on a menu...
46234         Roo.menu.MenuMgr.hideAll();
46235
46236         //this.editorsyncValue();
46237     },
46238    
46239     
46240     createFontOptions : function(){
46241         var buf = [], fs = this.fontFamilies, ff, lc;
46242         
46243         
46244         
46245         for(var i = 0, len = fs.length; i< len; i++){
46246             ff = fs[i];
46247             lc = ff.toLowerCase();
46248             buf.push(
46249                 '<option value="',lc,'" style="font-family:',ff,';"',
46250                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46251                     ff,
46252                 '</option>'
46253             );
46254         }
46255         return buf.join('');
46256     },
46257     
46258     toggleSourceEdit : function(sourceEditMode){
46259         
46260         Roo.log("toolbar toogle");
46261         if(sourceEditMode === undefined){
46262             sourceEditMode = !this.sourceEditMode;
46263         }
46264         this.sourceEditMode = sourceEditMode === true;
46265         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46266         // just toggle the button?
46267         if(btn.pressed !== this.sourceEditMode){
46268             btn.toggle(this.sourceEditMode);
46269             return;
46270         }
46271         
46272         if(sourceEditMode){
46273             Roo.log("disabling buttons");
46274             this.tb.items.each(function(item){
46275                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46276                     item.disable();
46277                 }
46278             });
46279           
46280         }else{
46281             Roo.log("enabling buttons");
46282             if(this.editorcore.initialized){
46283                 this.tb.items.each(function(item){
46284                     item.enable();
46285                 });
46286             }
46287             
46288         }
46289         Roo.log("calling toggole on editor");
46290         // tell the editor that it's been pressed..
46291         this.editor.toggleSourceEdit(sourceEditMode);
46292        
46293     },
46294      /**
46295      * Object collection of toolbar tooltips for the buttons in the editor. The key
46296      * is the command id associated with that button and the value is a valid QuickTips object.
46297      * For example:
46298 <pre><code>
46299 {
46300     bold : {
46301         title: 'Bold (Ctrl+B)',
46302         text: 'Make the selected text bold.',
46303         cls: 'x-html-editor-tip'
46304     },
46305     italic : {
46306         title: 'Italic (Ctrl+I)',
46307         text: 'Make the selected text italic.',
46308         cls: 'x-html-editor-tip'
46309     },
46310     ...
46311 </code></pre>
46312     * @type Object
46313      */
46314     buttonTips : {
46315         bold : {
46316             title: 'Bold (Ctrl+B)',
46317             text: 'Make the selected text bold.',
46318             cls: 'x-html-editor-tip'
46319         },
46320         italic : {
46321             title: 'Italic (Ctrl+I)',
46322             text: 'Make the selected text italic.',
46323             cls: 'x-html-editor-tip'
46324         },
46325         underline : {
46326             title: 'Underline (Ctrl+U)',
46327             text: 'Underline the selected text.',
46328             cls: 'x-html-editor-tip'
46329         },
46330         strikethrough : {
46331             title: 'Strikethrough',
46332             text: 'Strikethrough the selected text.',
46333             cls: 'x-html-editor-tip'
46334         },
46335         increasefontsize : {
46336             title: 'Grow Text',
46337             text: 'Increase the font size.',
46338             cls: 'x-html-editor-tip'
46339         },
46340         decreasefontsize : {
46341             title: 'Shrink Text',
46342             text: 'Decrease the font size.',
46343             cls: 'x-html-editor-tip'
46344         },
46345         backcolor : {
46346             title: 'Text Highlight Color',
46347             text: 'Change the background color of the selected text.',
46348             cls: 'x-html-editor-tip'
46349         },
46350         forecolor : {
46351             title: 'Font Color',
46352             text: 'Change the color of the selected text.',
46353             cls: 'x-html-editor-tip'
46354         },
46355         justifyleft : {
46356             title: 'Align Text Left',
46357             text: 'Align text to the left.',
46358             cls: 'x-html-editor-tip'
46359         },
46360         justifycenter : {
46361             title: 'Center Text',
46362             text: 'Center text in the editor.',
46363             cls: 'x-html-editor-tip'
46364         },
46365         justifyright : {
46366             title: 'Align Text Right',
46367             text: 'Align text to the right.',
46368             cls: 'x-html-editor-tip'
46369         },
46370         insertunorderedlist : {
46371             title: 'Bullet List',
46372             text: 'Start a bulleted list.',
46373             cls: 'x-html-editor-tip'
46374         },
46375         insertorderedlist : {
46376             title: 'Numbered List',
46377             text: 'Start a numbered list.',
46378             cls: 'x-html-editor-tip'
46379         },
46380         createlink : {
46381             title: 'Hyperlink',
46382             text: 'Make the selected text a hyperlink.',
46383             cls: 'x-html-editor-tip'
46384         },
46385         sourceedit : {
46386             title: 'Source Edit',
46387             text: 'Switch to source editing mode.',
46388             cls: 'x-html-editor-tip'
46389         }
46390     },
46391     // private
46392     onDestroy : function(){
46393         if(this.rendered){
46394             
46395             this.tb.items.each(function(item){
46396                 if(item.menu){
46397                     item.menu.removeAll();
46398                     if(item.menu.el){
46399                         item.menu.el.destroy();
46400                     }
46401                 }
46402                 item.destroy();
46403             });
46404              
46405         }
46406     },
46407     onFirstFocus: function() {
46408         this.tb.items.each(function(item){
46409            item.enable();
46410         });
46411     }
46412 });
46413
46414
46415
46416
46417 // <script type="text/javascript">
46418 /*
46419  * Based on
46420  * Ext JS Library 1.1.1
46421  * Copyright(c) 2006-2007, Ext JS, LLC.
46422  *  
46423  
46424  */
46425
46426  
46427 /**
46428  * @class Roo.form.HtmlEditor.ToolbarContext
46429  * Context Toolbar
46430  * 
46431  * Usage:
46432  *
46433  new Roo.form.HtmlEditor({
46434     ....
46435     toolbars : [
46436         { xtype: 'ToolbarStandard', styles : {} }
46437         { xtype: 'ToolbarContext', disable : {} }
46438     ]
46439 })
46440
46441      
46442  * 
46443  * @config : {Object} disable List of elements to disable.. (not done yet.)
46444  * @config : {Object} styles  Map of styles available.
46445  * 
46446  */
46447
46448 Roo.form.HtmlEditor.ToolbarContext = function(config)
46449 {
46450     
46451     Roo.apply(this, config);
46452     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46453     // dont call parent... till later.
46454     this.styles = this.styles || {};
46455 }
46456
46457  
46458
46459 Roo.form.HtmlEditor.ToolbarContext.types = {
46460     'IMG' : {
46461         width : {
46462             title: "Width",
46463             width: 40
46464         },
46465         height:  {
46466             title: "Height",
46467             width: 40
46468         },
46469         align: {
46470             title: "Align",
46471             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46472             width : 80
46473             
46474         },
46475         border: {
46476             title: "Border",
46477             width: 40
46478         },
46479         alt: {
46480             title: "Alt",
46481             width: 120
46482         },
46483         src : {
46484             title: "Src",
46485             width: 220
46486         }
46487         
46488     },
46489     'A' : {
46490         name : {
46491             title: "Name",
46492             width: 50
46493         },
46494         target:  {
46495             title: "Target",
46496             width: 120
46497         },
46498         href:  {
46499             title: "Href",
46500             width: 220
46501         } // border?
46502         
46503     },
46504     'TABLE' : {
46505         rows : {
46506             title: "Rows",
46507             width: 20
46508         },
46509         cols : {
46510             title: "Cols",
46511             width: 20
46512         },
46513         width : {
46514             title: "Width",
46515             width: 40
46516         },
46517         height : {
46518             title: "Height",
46519             width: 40
46520         },
46521         border : {
46522             title: "Border",
46523             width: 20
46524         }
46525     },
46526     'TD' : {
46527         width : {
46528             title: "Width",
46529             width: 40
46530         },
46531         height : {
46532             title: "Height",
46533             width: 40
46534         },   
46535         align: {
46536             title: "Align",
46537             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46538             width: 80
46539         },
46540         valign: {
46541             title: "Valign",
46542             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46543             width: 80
46544         },
46545         colspan: {
46546             title: "Colspan",
46547             width: 20
46548             
46549         },
46550          'font-family'  : {
46551             title : "Font",
46552             style : 'fontFamily',
46553             displayField: 'display',
46554             optname : 'font-family',
46555             width: 140
46556         }
46557     },
46558     'INPUT' : {
46559         name : {
46560             title: "name",
46561             width: 120
46562         },
46563         value : {
46564             title: "Value",
46565             width: 120
46566         },
46567         width : {
46568             title: "Width",
46569             width: 40
46570         }
46571     },
46572     'LABEL' : {
46573         'for' : {
46574             title: "For",
46575             width: 120
46576         }
46577     },
46578     'TEXTAREA' : {
46579           name : {
46580             title: "name",
46581             width: 120
46582         },
46583         rows : {
46584             title: "Rows",
46585             width: 20
46586         },
46587         cols : {
46588             title: "Cols",
46589             width: 20
46590         }
46591     },
46592     'SELECT' : {
46593         name : {
46594             title: "name",
46595             width: 120
46596         },
46597         selectoptions : {
46598             title: "Options",
46599             width: 200
46600         }
46601     },
46602     
46603     // should we really allow this??
46604     // should this just be 
46605     'BODY' : {
46606         title : {
46607             title: "Title",
46608             width: 200,
46609             disabled : true
46610         }
46611     },
46612     'SPAN' : {
46613         'font-family'  : {
46614             title : "Font",
46615             style : 'fontFamily',
46616             displayField: 'display',
46617             optname : 'font-family',
46618             width: 140
46619         }
46620     },
46621     'DIV' : {
46622         'font-family'  : {
46623             title : "Font",
46624             style : 'fontFamily',
46625             displayField: 'display',
46626             optname : 'font-family',
46627             width: 140
46628         }
46629     },
46630      'P' : {
46631         'font-family'  : {
46632             title : "Font",
46633             style : 'fontFamily',
46634             displayField: 'display',
46635             optname : 'font-family',
46636             width: 140
46637         }
46638     },
46639     
46640     '*' : {
46641         // empty..
46642     }
46643
46644 };
46645
46646 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46647 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46648
46649 Roo.form.HtmlEditor.ToolbarContext.options = {
46650         'font-family'  : [ 
46651                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46652                 [ 'Courier New', 'Courier New'],
46653                 [ 'Tahoma', 'Tahoma'],
46654                 [ 'Times New Roman,serif', 'Times'],
46655                 [ 'Verdana','Verdana' ]
46656         ]
46657 };
46658
46659 // fixme - these need to be configurable..
46660  
46661
46662 //Roo.form.HtmlEditor.ToolbarContext.types
46663
46664
46665 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46666     
46667     tb: false,
46668     
46669     rendered: false,
46670     
46671     editor : false,
46672     editorcore : false,
46673     /**
46674      * @cfg {Object} disable  List of toolbar elements to disable
46675          
46676      */
46677     disable : false,
46678     /**
46679      * @cfg {Object} styles List of styles 
46680      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46681      *
46682      * These must be defined in the page, so they get rendered correctly..
46683      * .headline { }
46684      * TD.underline { }
46685      * 
46686      */
46687     styles : false,
46688     
46689     options: false,
46690     
46691     toolbars : false,
46692     
46693     init : function(editor)
46694     {
46695         this.editor = editor;
46696         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46697         var editorcore = this.editorcore;
46698         
46699         var fid = editorcore.frameId;
46700         var etb = this;
46701         function btn(id, toggle, handler){
46702             var xid = fid + '-'+ id ;
46703             return {
46704                 id : xid,
46705                 cmd : id,
46706                 cls : 'x-btn-icon x-edit-'+id,
46707                 enableToggle:toggle !== false,
46708                 scope: editorcore, // was editor...
46709                 handler:handler||editorcore.relayBtnCmd,
46710                 clickEvent:'mousedown',
46711                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46712                 tabIndex:-1
46713             };
46714         }
46715         // create a new element.
46716         var wdiv = editor.wrap.createChild({
46717                 tag: 'div'
46718             }, editor.wrap.dom.firstChild.nextSibling, true);
46719         
46720         // can we do this more than once??
46721         
46722          // stop form submits
46723       
46724  
46725         // disable everything...
46726         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46727         this.toolbars = {};
46728            
46729         for (var i in  ty) {
46730           
46731             this.toolbars[i] = this.buildToolbar(ty[i],i);
46732         }
46733         this.tb = this.toolbars.BODY;
46734         this.tb.el.show();
46735         this.buildFooter();
46736         this.footer.show();
46737         editor.on('hide', function( ) { this.footer.hide() }, this);
46738         editor.on('show', function( ) { this.footer.show() }, this);
46739         
46740          
46741         this.rendered = true;
46742         
46743         // the all the btns;
46744         editor.on('editorevent', this.updateToolbar, this);
46745         // other toolbars need to implement this..
46746         //editor.on('editmodechange', this.updateToolbar, this);
46747     },
46748     
46749     
46750     
46751     /**
46752      * Protected method that will not generally be called directly. It triggers
46753      * a toolbar update by reading the markup state of the current selection in the editor.
46754      *
46755      * Note you can force an update by calling on('editorevent', scope, false)
46756      */
46757     updateToolbar: function(editor,ev,sel){
46758
46759         //Roo.log(ev);
46760         // capture mouse up - this is handy for selecting images..
46761         // perhaps should go somewhere else...
46762         if(!this.editorcore.activated){
46763              this.editor.onFirstFocus();
46764             return;
46765         }
46766         
46767         
46768         
46769         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46770         // selectNode - might want to handle IE?
46771         if (ev &&
46772             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46773             ev.target && ev.target.tagName == 'IMG') {
46774             // they have click on an image...
46775             // let's see if we can change the selection...
46776             sel = ev.target;
46777          
46778               var nodeRange = sel.ownerDocument.createRange();
46779             try {
46780                 nodeRange.selectNode(sel);
46781             } catch (e) {
46782                 nodeRange.selectNodeContents(sel);
46783             }
46784             //nodeRange.collapse(true);
46785             var s = this.editorcore.win.getSelection();
46786             s.removeAllRanges();
46787             s.addRange(nodeRange);
46788         }  
46789         
46790       
46791         var updateFooter = sel ? false : true;
46792         
46793         
46794         var ans = this.editorcore.getAllAncestors();
46795         
46796         // pick
46797         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46798         
46799         if (!sel) { 
46800             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46801             sel = sel ? sel : this.editorcore.doc.body;
46802             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46803             
46804         }
46805         // pick a menu that exists..
46806         var tn = sel.tagName.toUpperCase();
46807         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46808         
46809         tn = sel.tagName.toUpperCase();
46810         
46811         var lastSel = this.tb.selectedNode;
46812         
46813         this.tb.selectedNode = sel;
46814         
46815         // if current menu does not match..
46816         
46817         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46818                 
46819             this.tb.el.hide();
46820             ///console.log("show: " + tn);
46821             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46822             this.tb.el.show();
46823             // update name
46824             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46825             
46826             
46827             // update attributes
46828             if (this.tb.fields) {
46829                 this.tb.fields.each(function(e) {
46830                     if (e.stylename) {
46831                         e.setValue(sel.style[e.stylename]);
46832                         return;
46833                     } 
46834                    e.setValue(sel.getAttribute(e.attrname));
46835                 });
46836             }
46837             
46838             var hasStyles = false;
46839             for(var i in this.styles) {
46840                 hasStyles = true;
46841                 break;
46842             }
46843             
46844             // update styles
46845             if (hasStyles) { 
46846                 var st = this.tb.fields.item(0);
46847                 
46848                 st.store.removeAll();
46849                
46850                 
46851                 var cn = sel.className.split(/\s+/);
46852                 
46853                 var avs = [];
46854                 if (this.styles['*']) {
46855                     
46856                     Roo.each(this.styles['*'], function(v) {
46857                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46858                     });
46859                 }
46860                 if (this.styles[tn]) { 
46861                     Roo.each(this.styles[tn], function(v) {
46862                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46863                     });
46864                 }
46865                 
46866                 st.store.loadData(avs);
46867                 st.collapse();
46868                 st.setValue(cn);
46869             }
46870             // flag our selected Node.
46871             this.tb.selectedNode = sel;
46872            
46873            
46874             Roo.menu.MenuMgr.hideAll();
46875
46876         }
46877         
46878         if (!updateFooter) {
46879             //this.footDisp.dom.innerHTML = ''; 
46880             return;
46881         }
46882         // update the footer
46883         //
46884         var html = '';
46885         
46886         this.footerEls = ans.reverse();
46887         Roo.each(this.footerEls, function(a,i) {
46888             if (!a) { return; }
46889             html += html.length ? ' &gt; '  :  '';
46890             
46891             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46892             
46893         });
46894        
46895         // 
46896         var sz = this.footDisp.up('td').getSize();
46897         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46898         this.footDisp.dom.style.marginLeft = '5px';
46899         
46900         this.footDisp.dom.style.overflow = 'hidden';
46901         
46902         this.footDisp.dom.innerHTML = html;
46903             
46904         //this.editorsyncValue();
46905     },
46906      
46907     
46908    
46909        
46910     // private
46911     onDestroy : function(){
46912         if(this.rendered){
46913             
46914             this.tb.items.each(function(item){
46915                 if(item.menu){
46916                     item.menu.removeAll();
46917                     if(item.menu.el){
46918                         item.menu.el.destroy();
46919                     }
46920                 }
46921                 item.destroy();
46922             });
46923              
46924         }
46925     },
46926     onFirstFocus: function() {
46927         // need to do this for all the toolbars..
46928         this.tb.items.each(function(item){
46929            item.enable();
46930         });
46931     },
46932     buildToolbar: function(tlist, nm)
46933     {
46934         var editor = this.editor;
46935         var editorcore = this.editorcore;
46936          // create a new element.
46937         var wdiv = editor.wrap.createChild({
46938                 tag: 'div'
46939             }, editor.wrap.dom.firstChild.nextSibling, true);
46940         
46941        
46942         var tb = new Roo.Toolbar(wdiv);
46943         // add the name..
46944         
46945         tb.add(nm+ ":&nbsp;");
46946         
46947         var styles = [];
46948         for(var i in this.styles) {
46949             styles.push(i);
46950         }
46951         
46952         // styles...
46953         if (styles && styles.length) {
46954             
46955             // this needs a multi-select checkbox...
46956             tb.addField( new Roo.form.ComboBox({
46957                 store: new Roo.data.SimpleStore({
46958                     id : 'val',
46959                     fields: ['val', 'selected'],
46960                     data : [] 
46961                 }),
46962                 name : '-roo-edit-className',
46963                 attrname : 'className',
46964                 displayField: 'val',
46965                 typeAhead: false,
46966                 mode: 'local',
46967                 editable : false,
46968                 triggerAction: 'all',
46969                 emptyText:'Select Style',
46970                 selectOnFocus:true,
46971                 width: 130,
46972                 listeners : {
46973                     'select': function(c, r, i) {
46974                         // initial support only for on class per el..
46975                         tb.selectedNode.className =  r ? r.get('val') : '';
46976                         editorcore.syncValue();
46977                     }
46978                 }
46979     
46980             }));
46981         }
46982         
46983         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46984         var tbops = tbc.options;
46985         
46986         for (var i in tlist) {
46987             
46988             var item = tlist[i];
46989             tb.add(item.title + ":&nbsp;");
46990             
46991             
46992             //optname == used so you can configure the options available..
46993             var opts = item.opts ? item.opts : false;
46994             if (item.optname) {
46995                 opts = tbops[item.optname];
46996            
46997             }
46998             
46999             if (opts) {
47000                 // opts == pulldown..
47001                 tb.addField( new Roo.form.ComboBox({
47002                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47003                         id : 'val',
47004                         fields: ['val', 'display'],
47005                         data : opts  
47006                     }),
47007                     name : '-roo-edit-' + i,
47008                     attrname : i,
47009                     stylename : item.style ? item.style : false,
47010                     displayField: item.displayField ? item.displayField : 'val',
47011                     valueField :  'val',
47012                     typeAhead: false,
47013                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47014                     editable : false,
47015                     triggerAction: 'all',
47016                     emptyText:'Select',
47017                     selectOnFocus:true,
47018                     width: item.width ? item.width  : 130,
47019                     listeners : {
47020                         'select': function(c, r, i) {
47021                             if (c.stylename) {
47022                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47023                                 return;
47024                             }
47025                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47026                         }
47027                     }
47028
47029                 }));
47030                 continue;
47031                     
47032                  
47033                 
47034                 tb.addField( new Roo.form.TextField({
47035                     name: i,
47036                     width: 100,
47037                     //allowBlank:false,
47038                     value: ''
47039                 }));
47040                 continue;
47041             }
47042             tb.addField( new Roo.form.TextField({
47043                 name: '-roo-edit-' + i,
47044                 attrname : i,
47045                 
47046                 width: item.width,
47047                 //allowBlank:true,
47048                 value: '',
47049                 listeners: {
47050                     'change' : function(f, nv, ov) {
47051                         tb.selectedNode.setAttribute(f.attrname, nv);
47052                         editorcore.syncValue();
47053                     }
47054                 }
47055             }));
47056              
47057         }
47058         
47059         var _this = this;
47060         
47061         if(nm == 'BODY'){
47062             tb.addSeparator();
47063         
47064             tb.addButton( {
47065                 text: 'Stylesheets',
47066
47067                 listeners : {
47068                     click : function ()
47069                     {
47070                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47071                     }
47072                 }
47073             });
47074         }
47075         
47076         tb.addFill();
47077         tb.addButton( {
47078             text: 'Remove Tag',
47079     
47080             listeners : {
47081                 click : function ()
47082                 {
47083                     // remove
47084                     // undo does not work.
47085                      
47086                     var sn = tb.selectedNode;
47087                     
47088                     var pn = sn.parentNode;
47089                     
47090                     var stn =  sn.childNodes[0];
47091                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47092                     while (sn.childNodes.length) {
47093                         var node = sn.childNodes[0];
47094                         sn.removeChild(node);
47095                         //Roo.log(node);
47096                         pn.insertBefore(node, sn);
47097                         
47098                     }
47099                     pn.removeChild(sn);
47100                     var range = editorcore.createRange();
47101         
47102                     range.setStart(stn,0);
47103                     range.setEnd(en,0); //????
47104                     //range.selectNode(sel);
47105                     
47106                     
47107                     var selection = editorcore.getSelection();
47108                     selection.removeAllRanges();
47109                     selection.addRange(range);
47110                     
47111                     
47112                     
47113                     //_this.updateToolbar(null, null, pn);
47114                     _this.updateToolbar(null, null, null);
47115                     _this.footDisp.dom.innerHTML = ''; 
47116                 }
47117             }
47118             
47119                     
47120                 
47121             
47122         });
47123         
47124         
47125         tb.el.on('click', function(e){
47126             e.preventDefault(); // what does this do?
47127         });
47128         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47129         tb.el.hide();
47130         tb.name = nm;
47131         // dont need to disable them... as they will get hidden
47132         return tb;
47133          
47134         
47135     },
47136     buildFooter : function()
47137     {
47138         
47139         var fel = this.editor.wrap.createChild();
47140         this.footer = new Roo.Toolbar(fel);
47141         // toolbar has scrolly on left / right?
47142         var footDisp= new Roo.Toolbar.Fill();
47143         var _t = this;
47144         this.footer.add(
47145             {
47146                 text : '&lt;',
47147                 xtype: 'Button',
47148                 handler : function() {
47149                     _t.footDisp.scrollTo('left',0,true)
47150                 }
47151             }
47152         );
47153         this.footer.add( footDisp );
47154         this.footer.add( 
47155             {
47156                 text : '&gt;',
47157                 xtype: 'Button',
47158                 handler : function() {
47159                     // no animation..
47160                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47161                 }
47162             }
47163         );
47164         var fel = Roo.get(footDisp.el);
47165         fel.addClass('x-editor-context');
47166         this.footDispWrap = fel; 
47167         this.footDispWrap.overflow  = 'hidden';
47168         
47169         this.footDisp = fel.createChild();
47170         this.footDispWrap.on('click', this.onContextClick, this)
47171         
47172         
47173     },
47174     onContextClick : function (ev,dom)
47175     {
47176         ev.preventDefault();
47177         var  cn = dom.className;
47178         //Roo.log(cn);
47179         if (!cn.match(/x-ed-loc-/)) {
47180             return;
47181         }
47182         var n = cn.split('-').pop();
47183         var ans = this.footerEls;
47184         var sel = ans[n];
47185         
47186          // pick
47187         var range = this.editorcore.createRange();
47188         
47189         range.selectNodeContents(sel);
47190         //range.selectNode(sel);
47191         
47192         
47193         var selection = this.editorcore.getSelection();
47194         selection.removeAllRanges();
47195         selection.addRange(range);
47196         
47197         
47198         
47199         this.updateToolbar(null, null, sel);
47200         
47201         
47202     }
47203     
47204     
47205     
47206     
47207     
47208 });
47209
47210
47211
47212
47213
47214 /*
47215  * Based on:
47216  * Ext JS Library 1.1.1
47217  * Copyright(c) 2006-2007, Ext JS, LLC.
47218  *
47219  * Originally Released Under LGPL - original licence link has changed is not relivant.
47220  *
47221  * Fork - LGPL
47222  * <script type="text/javascript">
47223  */
47224  
47225 /**
47226  * @class Roo.form.BasicForm
47227  * @extends Roo.util.Observable
47228  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47229  * @constructor
47230  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47231  * @param {Object} config Configuration options
47232  */
47233 Roo.form.BasicForm = function(el, config){
47234     this.allItems = [];
47235     this.childForms = [];
47236     Roo.apply(this, config);
47237     /*
47238      * The Roo.form.Field items in this form.
47239      * @type MixedCollection
47240      */
47241      
47242      
47243     this.items = new Roo.util.MixedCollection(false, function(o){
47244         return o.id || (o.id = Roo.id());
47245     });
47246     this.addEvents({
47247         /**
47248          * @event beforeaction
47249          * Fires before any action is performed. Return false to cancel the action.
47250          * @param {Form} this
47251          * @param {Action} action The action to be performed
47252          */
47253         beforeaction: true,
47254         /**
47255          * @event actionfailed
47256          * Fires when an action fails.
47257          * @param {Form} this
47258          * @param {Action} action The action that failed
47259          */
47260         actionfailed : true,
47261         /**
47262          * @event actioncomplete
47263          * Fires when an action is completed.
47264          * @param {Form} this
47265          * @param {Action} action The action that completed
47266          */
47267         actioncomplete : true
47268     });
47269     if(el){
47270         this.initEl(el);
47271     }
47272     Roo.form.BasicForm.superclass.constructor.call(this);
47273     
47274     Roo.form.BasicForm.popover.apply();
47275 };
47276
47277 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47278     /**
47279      * @cfg {String} method
47280      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47281      */
47282     /**
47283      * @cfg {DataReader} reader
47284      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47285      * This is optional as there is built-in support for processing JSON.
47286      */
47287     /**
47288      * @cfg {DataReader} errorReader
47289      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47290      * This is completely optional as there is built-in support for processing JSON.
47291      */
47292     /**
47293      * @cfg {String} url
47294      * The URL to use for form actions if one isn't supplied in the action options.
47295      */
47296     /**
47297      * @cfg {Boolean} fileUpload
47298      * Set to true if this form is a file upload.
47299      */
47300      
47301     /**
47302      * @cfg {Object} baseParams
47303      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47304      */
47305      /**
47306      
47307     /**
47308      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47309      */
47310     timeout: 30,
47311
47312     // private
47313     activeAction : null,
47314
47315     /**
47316      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47317      * or setValues() data instead of when the form was first created.
47318      */
47319     trackResetOnLoad : false,
47320     
47321     
47322     /**
47323      * childForms - used for multi-tab forms
47324      * @type {Array}
47325      */
47326     childForms : false,
47327     
47328     /**
47329      * allItems - full list of fields.
47330      * @type {Array}
47331      */
47332     allItems : false,
47333     
47334     /**
47335      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47336      * element by passing it or its id or mask the form itself by passing in true.
47337      * @type Mixed
47338      */
47339     waitMsgTarget : false,
47340     
47341     /**
47342      * @type Boolean
47343      */
47344     disableMask : false,
47345     
47346     /**
47347      * @cfg {Boolean} errorMask (true|false) default false
47348      */
47349     errorMask : false,
47350     
47351     /**
47352      * @cfg {Number} maskOffset Default 100
47353      */
47354     maskOffset : 100,
47355
47356     // private
47357     initEl : function(el){
47358         this.el = Roo.get(el);
47359         this.id = this.el.id || Roo.id();
47360         this.el.on('submit', this.onSubmit, this);
47361         this.el.addClass('x-form');
47362     },
47363
47364     // private
47365     onSubmit : function(e){
47366         e.stopEvent();
47367     },
47368
47369     /**
47370      * Returns true if client-side validation on the form is successful.
47371      * @return Boolean
47372      */
47373     isValid : function(){
47374         var valid = true;
47375         var target = false;
47376         this.items.each(function(f){
47377             if(f.validate()){
47378                 return;
47379             }
47380             
47381             valid = false;
47382                 
47383             if(!target && f.el.isVisible(true)){
47384                 target = f;
47385             }
47386         });
47387         
47388         if(this.errorMask && !valid){
47389             Roo.form.BasicForm.popover.mask(this, target);
47390         }
47391         
47392         return valid;
47393     },
47394
47395     /**
47396      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47397      * @return Boolean
47398      */
47399     isDirty : function(){
47400         var dirty = false;
47401         this.items.each(function(f){
47402            if(f.isDirty()){
47403                dirty = true;
47404                return false;
47405            }
47406         });
47407         return dirty;
47408     },
47409     
47410     /**
47411      * Returns true if any fields in this form have changed since their original load. (New version)
47412      * @return Boolean
47413      */
47414     
47415     hasChanged : function()
47416     {
47417         var dirty = false;
47418         this.items.each(function(f){
47419            if(f.hasChanged()){
47420                dirty = true;
47421                return false;
47422            }
47423         });
47424         return dirty;
47425         
47426     },
47427     /**
47428      * Resets all hasChanged to 'false' -
47429      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47430      * So hasChanged storage is only to be used for this purpose
47431      * @return Boolean
47432      */
47433     resetHasChanged : function()
47434     {
47435         this.items.each(function(f){
47436            f.resetHasChanged();
47437         });
47438         
47439     },
47440     
47441     
47442     /**
47443      * Performs a predefined action (submit or load) or custom actions you define on this form.
47444      * @param {String} actionName The name of the action type
47445      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47446      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47447      * accept other config options):
47448      * <pre>
47449 Property          Type             Description
47450 ----------------  ---------------  ----------------------------------------------------------------------------------
47451 url               String           The url for the action (defaults to the form's url)
47452 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47453 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47454 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47455                                    validate the form on the client (defaults to false)
47456      * </pre>
47457      * @return {BasicForm} this
47458      */
47459     doAction : function(action, options){
47460         if(typeof action == 'string'){
47461             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47462         }
47463         if(this.fireEvent('beforeaction', this, action) !== false){
47464             this.beforeAction(action);
47465             action.run.defer(100, action);
47466         }
47467         return this;
47468     },
47469
47470     /**
47471      * Shortcut to do a submit action.
47472      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47473      * @return {BasicForm} this
47474      */
47475     submit : function(options){
47476         this.doAction('submit', options);
47477         return this;
47478     },
47479
47480     /**
47481      * Shortcut to do a load action.
47482      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47483      * @return {BasicForm} this
47484      */
47485     load : function(options){
47486         this.doAction('load', options);
47487         return this;
47488     },
47489
47490     /**
47491      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47492      * @param {Record} record The record to edit
47493      * @return {BasicForm} this
47494      */
47495     updateRecord : function(record){
47496         record.beginEdit();
47497         var fs = record.fields;
47498         fs.each(function(f){
47499             var field = this.findField(f.name);
47500             if(field){
47501                 record.set(f.name, field.getValue());
47502             }
47503         }, this);
47504         record.endEdit();
47505         return this;
47506     },
47507
47508     /**
47509      * Loads an Roo.data.Record into this form.
47510      * @param {Record} record The record to load
47511      * @return {BasicForm} this
47512      */
47513     loadRecord : function(record){
47514         this.setValues(record.data);
47515         return this;
47516     },
47517
47518     // private
47519     beforeAction : function(action){
47520         var o = action.options;
47521         
47522         if(!this.disableMask) {
47523             if(this.waitMsgTarget === true){
47524                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47525             }else if(this.waitMsgTarget){
47526                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47527                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47528             }else {
47529                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47530             }
47531         }
47532         
47533          
47534     },
47535
47536     // private
47537     afterAction : function(action, success){
47538         this.activeAction = null;
47539         var o = action.options;
47540         
47541         if(!this.disableMask) {
47542             if(this.waitMsgTarget === true){
47543                 this.el.unmask();
47544             }else if(this.waitMsgTarget){
47545                 this.waitMsgTarget.unmask();
47546             }else{
47547                 Roo.MessageBox.updateProgress(1);
47548                 Roo.MessageBox.hide();
47549             }
47550         }
47551         
47552         if(success){
47553             if(o.reset){
47554                 this.reset();
47555             }
47556             Roo.callback(o.success, o.scope, [this, action]);
47557             this.fireEvent('actioncomplete', this, action);
47558             
47559         }else{
47560             
47561             // failure condition..
47562             // we have a scenario where updates need confirming.
47563             // eg. if a locking scenario exists..
47564             // we look for { errors : { needs_confirm : true }} in the response.
47565             if (
47566                 (typeof(action.result) != 'undefined')  &&
47567                 (typeof(action.result.errors) != 'undefined')  &&
47568                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47569            ){
47570                 var _t = this;
47571                 Roo.MessageBox.confirm(
47572                     "Change requires confirmation",
47573                     action.result.errorMsg,
47574                     function(r) {
47575                         if (r != 'yes') {
47576                             return;
47577                         }
47578                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47579                     }
47580                     
47581                 );
47582                 
47583                 
47584                 
47585                 return;
47586             }
47587             
47588             Roo.callback(o.failure, o.scope, [this, action]);
47589             // show an error message if no failed handler is set..
47590             if (!this.hasListener('actionfailed')) {
47591                 Roo.MessageBox.alert("Error",
47592                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47593                         action.result.errorMsg :
47594                         "Saving Failed, please check your entries or try again"
47595                 );
47596             }
47597             
47598             this.fireEvent('actionfailed', this, action);
47599         }
47600         
47601     },
47602
47603     /**
47604      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47605      * @param {String} id The value to search for
47606      * @return Field
47607      */
47608     findField : function(id){
47609         var field = this.items.get(id);
47610         if(!field){
47611             this.items.each(function(f){
47612                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47613                     field = f;
47614                     return false;
47615                 }
47616             });
47617         }
47618         return field || null;
47619     },
47620
47621     /**
47622      * Add a secondary form to this one, 
47623      * Used to provide tabbed forms. One form is primary, with hidden values 
47624      * which mirror the elements from the other forms.
47625      * 
47626      * @param {Roo.form.Form} form to add.
47627      * 
47628      */
47629     addForm : function(form)
47630     {
47631        
47632         if (this.childForms.indexOf(form) > -1) {
47633             // already added..
47634             return;
47635         }
47636         this.childForms.push(form);
47637         var n = '';
47638         Roo.each(form.allItems, function (fe) {
47639             
47640             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47641             if (this.findField(n)) { // already added..
47642                 return;
47643             }
47644             var add = new Roo.form.Hidden({
47645                 name : n
47646             });
47647             add.render(this.el);
47648             
47649             this.add( add );
47650         }, this);
47651         
47652     },
47653     /**
47654      * Mark fields in this form invalid in bulk.
47655      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47656      * @return {BasicForm} this
47657      */
47658     markInvalid : function(errors){
47659         if(errors instanceof Array){
47660             for(var i = 0, len = errors.length; i < len; i++){
47661                 var fieldError = errors[i];
47662                 var f = this.findField(fieldError.id);
47663                 if(f){
47664                     f.markInvalid(fieldError.msg);
47665                 }
47666             }
47667         }else{
47668             var field, id;
47669             for(id in errors){
47670                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47671                     field.markInvalid(errors[id]);
47672                 }
47673             }
47674         }
47675         Roo.each(this.childForms || [], function (f) {
47676             f.markInvalid(errors);
47677         });
47678         
47679         return this;
47680     },
47681
47682     /**
47683      * Set values for fields in this form in bulk.
47684      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47685      * @return {BasicForm} this
47686      */
47687     setValues : function(values){
47688         if(values instanceof Array){ // array of objects
47689             for(var i = 0, len = values.length; i < len; i++){
47690                 var v = values[i];
47691                 var f = this.findField(v.id);
47692                 if(f){
47693                     f.setValue(v.value);
47694                     if(this.trackResetOnLoad){
47695                         f.originalValue = f.getValue();
47696                     }
47697                 }
47698             }
47699         }else{ // object hash
47700             var field, id;
47701             for(id in values){
47702                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47703                     
47704                     if (field.setFromData && 
47705                         field.valueField && 
47706                         field.displayField &&
47707                         // combos' with local stores can 
47708                         // be queried via setValue()
47709                         // to set their value..
47710                         (field.store && !field.store.isLocal)
47711                         ) {
47712                         // it's a combo
47713                         var sd = { };
47714                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47715                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47716                         field.setFromData(sd);
47717                         
47718                     } else {
47719                         field.setValue(values[id]);
47720                     }
47721                     
47722                     
47723                     if(this.trackResetOnLoad){
47724                         field.originalValue = field.getValue();
47725                     }
47726                 }
47727             }
47728         }
47729         this.resetHasChanged();
47730         
47731         
47732         Roo.each(this.childForms || [], function (f) {
47733             f.setValues(values);
47734             f.resetHasChanged();
47735         });
47736                 
47737         return this;
47738     },
47739  
47740     /**
47741      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47742      * they are returned as an array.
47743      * @param {Boolean} asString
47744      * @return {Object}
47745      */
47746     getValues : function(asString){
47747         if (this.childForms) {
47748             // copy values from the child forms
47749             Roo.each(this.childForms, function (f) {
47750                 this.setValues(f.getValues());
47751             }, this);
47752         }
47753         
47754         // use formdata
47755         if (typeof(FormData) != 'undefined' && asString !== true) {
47756             var fd = (new FormData(this.el.dom)).entries();
47757             var ret = {};
47758             var ent = fd.next();
47759             while (!ent.done) {
47760                 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
47761                 ent = fd.next();
47762             };
47763             return ret;
47764         }
47765         
47766         
47767         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47768         if(asString === true){
47769             return fs;
47770         }
47771         return Roo.urlDecode(fs);
47772     },
47773     
47774     /**
47775      * Returns the fields in this form as an object with key/value pairs. 
47776      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47777      * @return {Object}
47778      */
47779     getFieldValues : function(with_hidden)
47780     {
47781         if (this.childForms) {
47782             // copy values from the child forms
47783             // should this call getFieldValues - probably not as we do not currently copy
47784             // hidden fields when we generate..
47785             Roo.each(this.childForms, function (f) {
47786                 this.setValues(f.getValues());
47787             }, this);
47788         }
47789         
47790         var ret = {};
47791         this.items.each(function(f){
47792             if (!f.getName()) {
47793                 return;
47794             }
47795             var v = f.getValue();
47796             if (f.inputType =='radio') {
47797                 if (typeof(ret[f.getName()]) == 'undefined') {
47798                     ret[f.getName()] = ''; // empty..
47799                 }
47800                 
47801                 if (!f.el.dom.checked) {
47802                     return;
47803                     
47804                 }
47805                 v = f.el.dom.value;
47806                 
47807             }
47808             
47809             // not sure if this supported any more..
47810             if ((typeof(v) == 'object') && f.getRawValue) {
47811                 v = f.getRawValue() ; // dates..
47812             }
47813             // combo boxes where name != hiddenName...
47814             if (f.name != f.getName()) {
47815                 ret[f.name] = f.getRawValue();
47816             }
47817             ret[f.getName()] = v;
47818         });
47819         
47820         return ret;
47821     },
47822
47823     /**
47824      * Clears all invalid messages in this form.
47825      * @return {BasicForm} this
47826      */
47827     clearInvalid : function(){
47828         this.items.each(function(f){
47829            f.clearInvalid();
47830         });
47831         
47832         Roo.each(this.childForms || [], function (f) {
47833             f.clearInvalid();
47834         });
47835         
47836         
47837         return this;
47838     },
47839
47840     /**
47841      * Resets this form.
47842      * @return {BasicForm} this
47843      */
47844     reset : function(){
47845         this.items.each(function(f){
47846             f.reset();
47847         });
47848         
47849         Roo.each(this.childForms || [], function (f) {
47850             f.reset();
47851         });
47852         this.resetHasChanged();
47853         
47854         return this;
47855     },
47856
47857     /**
47858      * Add Roo.form components to this form.
47859      * @param {Field} field1
47860      * @param {Field} field2 (optional)
47861      * @param {Field} etc (optional)
47862      * @return {BasicForm} this
47863      */
47864     add : function(){
47865         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47866         return this;
47867     },
47868
47869
47870     /**
47871      * Removes a field from the items collection (does NOT remove its markup).
47872      * @param {Field} field
47873      * @return {BasicForm} this
47874      */
47875     remove : function(field){
47876         this.items.remove(field);
47877         return this;
47878     },
47879
47880     /**
47881      * Looks at the fields in this form, checks them for an id attribute,
47882      * and calls applyTo on the existing dom element with that id.
47883      * @return {BasicForm} this
47884      */
47885     render : function(){
47886         this.items.each(function(f){
47887             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47888                 f.applyTo(f.id);
47889             }
47890         });
47891         return this;
47892     },
47893
47894     /**
47895      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47896      * @param {Object} values
47897      * @return {BasicForm} this
47898      */
47899     applyToFields : function(o){
47900         this.items.each(function(f){
47901            Roo.apply(f, o);
47902         });
47903         return this;
47904     },
47905
47906     /**
47907      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47908      * @param {Object} values
47909      * @return {BasicForm} this
47910      */
47911     applyIfToFields : function(o){
47912         this.items.each(function(f){
47913            Roo.applyIf(f, o);
47914         });
47915         return this;
47916     }
47917 });
47918
47919 // back compat
47920 Roo.BasicForm = Roo.form.BasicForm;
47921
47922 Roo.apply(Roo.form.BasicForm, {
47923     
47924     popover : {
47925         
47926         padding : 5,
47927         
47928         isApplied : false,
47929         
47930         isMasked : false,
47931         
47932         form : false,
47933         
47934         target : false,
47935         
47936         intervalID : false,
47937         
47938         maskEl : false,
47939         
47940         apply : function()
47941         {
47942             if(this.isApplied){
47943                 return;
47944             }
47945             
47946             this.maskEl = {
47947                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
47948                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
47949                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
47950                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
47951             };
47952             
47953             this.maskEl.top.enableDisplayMode("block");
47954             this.maskEl.left.enableDisplayMode("block");
47955             this.maskEl.bottom.enableDisplayMode("block");
47956             this.maskEl.right.enableDisplayMode("block");
47957             
47958             Roo.get(document.body).on('click', function(){
47959                 this.unmask();
47960             }, this);
47961             
47962             Roo.get(document.body).on('touchstart', function(){
47963                 this.unmask();
47964             }, this);
47965             
47966             this.isApplied = true
47967         },
47968         
47969         mask : function(form, target)
47970         {
47971             this.form = form;
47972             
47973             this.target = target;
47974             
47975             if(!this.form.errorMask || !target.el){
47976                 return;
47977             }
47978             
47979             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
47980             
47981             var ot = this.target.el.calcOffsetsTo(scrollable);
47982             
47983             var scrollTo = ot[1] - this.form.maskOffset;
47984             
47985             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
47986             
47987             scrollable.scrollTo('top', scrollTo);
47988             
47989             var el = this.target.wrap || this.target.el;
47990             
47991             var box = el.getBox();
47992             
47993             this.maskEl.top.setStyle('position', 'absolute');
47994             this.maskEl.top.setStyle('z-index', 10000);
47995             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
47996             this.maskEl.top.setLeft(0);
47997             this.maskEl.top.setTop(0);
47998             this.maskEl.top.show();
47999             
48000             this.maskEl.left.setStyle('position', 'absolute');
48001             this.maskEl.left.setStyle('z-index', 10000);
48002             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48003             this.maskEl.left.setLeft(0);
48004             this.maskEl.left.setTop(box.y - this.padding);
48005             this.maskEl.left.show();
48006
48007             this.maskEl.bottom.setStyle('position', 'absolute');
48008             this.maskEl.bottom.setStyle('z-index', 10000);
48009             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48010             this.maskEl.bottom.setLeft(0);
48011             this.maskEl.bottom.setTop(box.bottom + this.padding);
48012             this.maskEl.bottom.show();
48013
48014             this.maskEl.right.setStyle('position', 'absolute');
48015             this.maskEl.right.setStyle('z-index', 10000);
48016             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48017             this.maskEl.right.setLeft(box.right + this.padding);
48018             this.maskEl.right.setTop(box.y - this.padding);
48019             this.maskEl.right.show();
48020
48021             this.intervalID = window.setInterval(function() {
48022                 Roo.form.BasicForm.popover.unmask();
48023             }, 10000);
48024
48025             window.onwheel = function(){ return false;};
48026             
48027             (function(){ this.isMasked = true; }).defer(500, this);
48028             
48029         },
48030         
48031         unmask : function()
48032         {
48033             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48034                 return;
48035             }
48036             
48037             this.maskEl.top.setStyle('position', 'absolute');
48038             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48039             this.maskEl.top.hide();
48040
48041             this.maskEl.left.setStyle('position', 'absolute');
48042             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48043             this.maskEl.left.hide();
48044
48045             this.maskEl.bottom.setStyle('position', 'absolute');
48046             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48047             this.maskEl.bottom.hide();
48048
48049             this.maskEl.right.setStyle('position', 'absolute');
48050             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48051             this.maskEl.right.hide();
48052             
48053             window.onwheel = function(){ return true;};
48054             
48055             if(this.intervalID){
48056                 window.clearInterval(this.intervalID);
48057                 this.intervalID = false;
48058             }
48059             
48060             this.isMasked = false;
48061             
48062         }
48063         
48064     }
48065     
48066 });/*
48067  * Based on:
48068  * Ext JS Library 1.1.1
48069  * Copyright(c) 2006-2007, Ext JS, LLC.
48070  *
48071  * Originally Released Under LGPL - original licence link has changed is not relivant.
48072  *
48073  * Fork - LGPL
48074  * <script type="text/javascript">
48075  */
48076
48077 /**
48078  * @class Roo.form.Form
48079  * @extends Roo.form.BasicForm
48080  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48081  * @constructor
48082  * @param {Object} config Configuration options
48083  */
48084 Roo.form.Form = function(config){
48085     var xitems =  [];
48086     if (config.items) {
48087         xitems = config.items;
48088         delete config.items;
48089     }
48090    
48091     
48092     Roo.form.Form.superclass.constructor.call(this, null, config);
48093     this.url = this.url || this.action;
48094     if(!this.root){
48095         this.root = new Roo.form.Layout(Roo.applyIf({
48096             id: Roo.id()
48097         }, config));
48098     }
48099     this.active = this.root;
48100     /**
48101      * Array of all the buttons that have been added to this form via {@link addButton}
48102      * @type Array
48103      */
48104     this.buttons = [];
48105     this.allItems = [];
48106     this.addEvents({
48107         /**
48108          * @event clientvalidation
48109          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48110          * @param {Form} this
48111          * @param {Boolean} valid true if the form has passed client-side validation
48112          */
48113         clientvalidation: true,
48114         /**
48115          * @event rendered
48116          * Fires when the form is rendered
48117          * @param {Roo.form.Form} form
48118          */
48119         rendered : true
48120     });
48121     
48122     if (this.progressUrl) {
48123             // push a hidden field onto the list of fields..
48124             this.addxtype( {
48125                     xns: Roo.form, 
48126                     xtype : 'Hidden', 
48127                     name : 'UPLOAD_IDENTIFIER' 
48128             });
48129         }
48130         
48131     
48132     Roo.each(xitems, this.addxtype, this);
48133     
48134 };
48135
48136 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48137     /**
48138      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48139      */
48140     /**
48141      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48142      */
48143     /**
48144      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48145      */
48146     buttonAlign:'center',
48147
48148     /**
48149      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48150      */
48151     minButtonWidth:75,
48152
48153     /**
48154      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48155      * This property cascades to child containers if not set.
48156      */
48157     labelAlign:'left',
48158
48159     /**
48160      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48161      * fires a looping event with that state. This is required to bind buttons to the valid
48162      * state using the config value formBind:true on the button.
48163      */
48164     monitorValid : false,
48165
48166     /**
48167      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48168      */
48169     monitorPoll : 200,
48170     
48171     /**
48172      * @cfg {String} progressUrl - Url to return progress data 
48173      */
48174     
48175     progressUrl : false,
48176     /**
48177      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48178      * sending a formdata with extra parameters - eg uploaded elements.
48179      */
48180     
48181     formData : false,
48182     
48183     /**
48184      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48185      * fields are added and the column is closed. If no fields are passed the column remains open
48186      * until end() is called.
48187      * @param {Object} config The config to pass to the column
48188      * @param {Field} field1 (optional)
48189      * @param {Field} field2 (optional)
48190      * @param {Field} etc (optional)
48191      * @return Column The column container object
48192      */
48193     column : function(c){
48194         var col = new Roo.form.Column(c);
48195         this.start(col);
48196         if(arguments.length > 1){ // duplicate code required because of Opera
48197             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48198             this.end();
48199         }
48200         return col;
48201     },
48202
48203     /**
48204      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48205      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48206      * until end() is called.
48207      * @param {Object} config The config to pass to the fieldset
48208      * @param {Field} field1 (optional)
48209      * @param {Field} field2 (optional)
48210      * @param {Field} etc (optional)
48211      * @return FieldSet The fieldset container object
48212      */
48213     fieldset : function(c){
48214         var fs = new Roo.form.FieldSet(c);
48215         this.start(fs);
48216         if(arguments.length > 1){ // duplicate code required because of Opera
48217             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48218             this.end();
48219         }
48220         return fs;
48221     },
48222
48223     /**
48224      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48225      * fields are added and the container is closed. If no fields are passed the container remains open
48226      * until end() is called.
48227      * @param {Object} config The config to pass to the Layout
48228      * @param {Field} field1 (optional)
48229      * @param {Field} field2 (optional)
48230      * @param {Field} etc (optional)
48231      * @return Layout The container object
48232      */
48233     container : function(c){
48234         var l = new Roo.form.Layout(c);
48235         this.start(l);
48236         if(arguments.length > 1){ // duplicate code required because of Opera
48237             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48238             this.end();
48239         }
48240         return l;
48241     },
48242
48243     /**
48244      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48245      * @param {Object} container A Roo.form.Layout or subclass of Layout
48246      * @return {Form} this
48247      */
48248     start : function(c){
48249         // cascade label info
48250         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48251         this.active.stack.push(c);
48252         c.ownerCt = this.active;
48253         this.active = c;
48254         return this;
48255     },
48256
48257     /**
48258      * Closes the current open container
48259      * @return {Form} this
48260      */
48261     end : function(){
48262         if(this.active == this.root){
48263             return this;
48264         }
48265         this.active = this.active.ownerCt;
48266         return this;
48267     },
48268
48269     /**
48270      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48271      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48272      * as the label of the field.
48273      * @param {Field} field1
48274      * @param {Field} field2 (optional)
48275      * @param {Field} etc. (optional)
48276      * @return {Form} this
48277      */
48278     add : function(){
48279         this.active.stack.push.apply(this.active.stack, arguments);
48280         this.allItems.push.apply(this.allItems,arguments);
48281         var r = [];
48282         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48283             if(a[i].isFormField){
48284                 r.push(a[i]);
48285             }
48286         }
48287         if(r.length > 0){
48288             Roo.form.Form.superclass.add.apply(this, r);
48289         }
48290         return this;
48291     },
48292     
48293
48294     
48295     
48296     
48297      /**
48298      * Find any element that has been added to a form, using it's ID or name
48299      * This can include framesets, columns etc. along with regular fields..
48300      * @param {String} id - id or name to find.
48301      
48302      * @return {Element} e - or false if nothing found.
48303      */
48304     findbyId : function(id)
48305     {
48306         var ret = false;
48307         if (!id) {
48308             return ret;
48309         }
48310         Roo.each(this.allItems, function(f){
48311             if (f.id == id || f.name == id ){
48312                 ret = f;
48313                 return false;
48314             }
48315         });
48316         return ret;
48317     },
48318
48319     
48320     
48321     /**
48322      * Render this form into the passed container. This should only be called once!
48323      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48324      * @return {Form} this
48325      */
48326     render : function(ct)
48327     {
48328         
48329         
48330         
48331         ct = Roo.get(ct);
48332         var o = this.autoCreate || {
48333             tag: 'form',
48334             method : this.method || 'POST',
48335             id : this.id || Roo.id()
48336         };
48337         this.initEl(ct.createChild(o));
48338
48339         this.root.render(this.el);
48340         
48341        
48342              
48343         this.items.each(function(f){
48344             f.render('x-form-el-'+f.id);
48345         });
48346
48347         if(this.buttons.length > 0){
48348             // tables are required to maintain order and for correct IE layout
48349             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48350                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48351                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48352             }}, null, true);
48353             var tr = tb.getElementsByTagName('tr')[0];
48354             for(var i = 0, len = this.buttons.length; i < len; i++) {
48355                 var b = this.buttons[i];
48356                 var td = document.createElement('td');
48357                 td.className = 'x-form-btn-td';
48358                 b.render(tr.appendChild(td));
48359             }
48360         }
48361         if(this.monitorValid){ // initialize after render
48362             this.startMonitoring();
48363         }
48364         this.fireEvent('rendered', this);
48365         return this;
48366     },
48367
48368     /**
48369      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48370      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48371      * object or a valid Roo.DomHelper element config
48372      * @param {Function} handler The function called when the button is clicked
48373      * @param {Object} scope (optional) The scope of the handler function
48374      * @return {Roo.Button}
48375      */
48376     addButton : function(config, handler, scope){
48377         var bc = {
48378             handler: handler,
48379             scope: scope,
48380             minWidth: this.minButtonWidth,
48381             hideParent:true
48382         };
48383         if(typeof config == "string"){
48384             bc.text = config;
48385         }else{
48386             Roo.apply(bc, config);
48387         }
48388         var btn = new Roo.Button(null, bc);
48389         this.buttons.push(btn);
48390         return btn;
48391     },
48392
48393      /**
48394      * Adds a series of form elements (using the xtype property as the factory method.
48395      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48396      * @param {Object} config 
48397      */
48398     
48399     addxtype : function()
48400     {
48401         var ar = Array.prototype.slice.call(arguments, 0);
48402         var ret = false;
48403         for(var i = 0; i < ar.length; i++) {
48404             if (!ar[i]) {
48405                 continue; // skip -- if this happends something invalid got sent, we 
48406                 // should ignore it, as basically that interface element will not show up
48407                 // and that should be pretty obvious!!
48408             }
48409             
48410             if (Roo.form[ar[i].xtype]) {
48411                 ar[i].form = this;
48412                 var fe = Roo.factory(ar[i], Roo.form);
48413                 if (!ret) {
48414                     ret = fe;
48415                 }
48416                 fe.form = this;
48417                 if (fe.store) {
48418                     fe.store.form = this;
48419                 }
48420                 if (fe.isLayout) {  
48421                          
48422                     this.start(fe);
48423                     this.allItems.push(fe);
48424                     if (fe.items && fe.addxtype) {
48425                         fe.addxtype.apply(fe, fe.items);
48426                         delete fe.items;
48427                     }
48428                      this.end();
48429                     continue;
48430                 }
48431                 
48432                 
48433                  
48434                 this.add(fe);
48435               //  console.log('adding ' + ar[i].xtype);
48436             }
48437             if (ar[i].xtype == 'Button') {  
48438                 //console.log('adding button');
48439                 //console.log(ar[i]);
48440                 this.addButton(ar[i]);
48441                 this.allItems.push(fe);
48442                 continue;
48443             }
48444             
48445             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48446                 alert('end is not supported on xtype any more, use items');
48447             //    this.end();
48448             //    //console.log('adding end');
48449             }
48450             
48451         }
48452         return ret;
48453     },
48454     
48455     /**
48456      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48457      * option "monitorValid"
48458      */
48459     startMonitoring : function(){
48460         if(!this.bound){
48461             this.bound = true;
48462             Roo.TaskMgr.start({
48463                 run : this.bindHandler,
48464                 interval : this.monitorPoll || 200,
48465                 scope: this
48466             });
48467         }
48468     },
48469
48470     /**
48471      * Stops monitoring of the valid state of this form
48472      */
48473     stopMonitoring : function(){
48474         this.bound = false;
48475     },
48476
48477     // private
48478     bindHandler : function(){
48479         if(!this.bound){
48480             return false; // stops binding
48481         }
48482         var valid = true;
48483         this.items.each(function(f){
48484             if(!f.isValid(true)){
48485                 valid = false;
48486                 return false;
48487             }
48488         });
48489         for(var i = 0, len = this.buttons.length; i < len; i++){
48490             var btn = this.buttons[i];
48491             if(btn.formBind === true && btn.disabled === valid){
48492                 btn.setDisabled(!valid);
48493             }
48494         }
48495         this.fireEvent('clientvalidation', this, valid);
48496     }
48497     
48498     
48499     
48500     
48501     
48502     
48503     
48504     
48505 });
48506
48507
48508 // back compat
48509 Roo.Form = Roo.form.Form;
48510 /*
48511  * Based on:
48512  * Ext JS Library 1.1.1
48513  * Copyright(c) 2006-2007, Ext JS, LLC.
48514  *
48515  * Originally Released Under LGPL - original licence link has changed is not relivant.
48516  *
48517  * Fork - LGPL
48518  * <script type="text/javascript">
48519  */
48520
48521 // as we use this in bootstrap.
48522 Roo.namespace('Roo.form');
48523  /**
48524  * @class Roo.form.Action
48525  * Internal Class used to handle form actions
48526  * @constructor
48527  * @param {Roo.form.BasicForm} el The form element or its id
48528  * @param {Object} config Configuration options
48529  */
48530
48531  
48532  
48533 // define the action interface
48534 Roo.form.Action = function(form, options){
48535     this.form = form;
48536     this.options = options || {};
48537 };
48538 /**
48539  * Client Validation Failed
48540  * @const 
48541  */
48542 Roo.form.Action.CLIENT_INVALID = 'client';
48543 /**
48544  * Server Validation Failed
48545  * @const 
48546  */
48547 Roo.form.Action.SERVER_INVALID = 'server';
48548  /**
48549  * Connect to Server Failed
48550  * @const 
48551  */
48552 Roo.form.Action.CONNECT_FAILURE = 'connect';
48553 /**
48554  * Reading Data from Server Failed
48555  * @const 
48556  */
48557 Roo.form.Action.LOAD_FAILURE = 'load';
48558
48559 Roo.form.Action.prototype = {
48560     type : 'default',
48561     failureType : undefined,
48562     response : undefined,
48563     result : undefined,
48564
48565     // interface method
48566     run : function(options){
48567
48568     },
48569
48570     // interface method
48571     success : function(response){
48572
48573     },
48574
48575     // interface method
48576     handleResponse : function(response){
48577
48578     },
48579
48580     // default connection failure
48581     failure : function(response){
48582         
48583         this.response = response;
48584         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48585         this.form.afterAction(this, false);
48586     },
48587
48588     processResponse : function(response){
48589         this.response = response;
48590         if(!response.responseText){
48591             return true;
48592         }
48593         this.result = this.handleResponse(response);
48594         return this.result;
48595     },
48596
48597     // utility functions used internally
48598     getUrl : function(appendParams){
48599         var url = this.options.url || this.form.url || this.form.el.dom.action;
48600         if(appendParams){
48601             var p = this.getParams();
48602             if(p){
48603                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48604             }
48605         }
48606         return url;
48607     },
48608
48609     getMethod : function(){
48610         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48611     },
48612
48613     getParams : function(){
48614         var bp = this.form.baseParams;
48615         var p = this.options.params;
48616         if(p){
48617             if(typeof p == "object"){
48618                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48619             }else if(typeof p == 'string' && bp){
48620                 p += '&' + Roo.urlEncode(bp);
48621             }
48622         }else if(bp){
48623             p = Roo.urlEncode(bp);
48624         }
48625         return p;
48626     },
48627
48628     createCallback : function(){
48629         return {
48630             success: this.success,
48631             failure: this.failure,
48632             scope: this,
48633             timeout: (this.form.timeout*1000),
48634             upload: this.form.fileUpload ? this.success : undefined
48635         };
48636     }
48637 };
48638
48639 Roo.form.Action.Submit = function(form, options){
48640     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
48641 };
48642
48643 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
48644     type : 'submit',
48645
48646     haveProgress : false,
48647     uploadComplete : false,
48648     
48649     // uploadProgress indicator.
48650     uploadProgress : function()
48651     {
48652         if (!this.form.progressUrl) {
48653             return;
48654         }
48655         
48656         if (!this.haveProgress) {
48657             Roo.MessageBox.progress("Uploading", "Uploading");
48658         }
48659         if (this.uploadComplete) {
48660            Roo.MessageBox.hide();
48661            return;
48662         }
48663         
48664         this.haveProgress = true;
48665    
48666         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
48667         
48668         var c = new Roo.data.Connection();
48669         c.request({
48670             url : this.form.progressUrl,
48671             params: {
48672                 id : uid
48673             },
48674             method: 'GET',
48675             success : function(req){
48676                //console.log(data);
48677                 var rdata = false;
48678                 var edata;
48679                 try  {
48680                    rdata = Roo.decode(req.responseText)
48681                 } catch (e) {
48682                     Roo.log("Invalid data from server..");
48683                     Roo.log(edata);
48684                     return;
48685                 }
48686                 if (!rdata || !rdata.success) {
48687                     Roo.log(rdata);
48688                     Roo.MessageBox.alert(Roo.encode(rdata));
48689                     return;
48690                 }
48691                 var data = rdata.data;
48692                 
48693                 if (this.uploadComplete) {
48694                    Roo.MessageBox.hide();
48695                    return;
48696                 }
48697                    
48698                 if (data){
48699                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
48700                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
48701                     );
48702                 }
48703                 this.uploadProgress.defer(2000,this);
48704             },
48705        
48706             failure: function(data) {
48707                 Roo.log('progress url failed ');
48708                 Roo.log(data);
48709             },
48710             scope : this
48711         });
48712            
48713     },
48714     
48715     
48716     run : function()
48717     {
48718         // run get Values on the form, so it syncs any secondary forms.
48719         this.form.getValues();
48720         
48721         var o = this.options;
48722         var method = this.getMethod();
48723         var isPost = method == 'POST';
48724         if(o.clientValidation === false || this.form.isValid()){
48725             
48726             if (this.form.progressUrl) {
48727                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
48728                     (new Date() * 1) + '' + Math.random());
48729                     
48730             } 
48731             
48732             
48733             Roo.Ajax.request(Roo.apply(this.createCallback(), {
48734                 form:this.form.el.dom,
48735                 url:this.getUrl(!isPost),
48736                 method: method,
48737                 params:isPost ? this.getParams() : null,
48738                 isUpload: this.form.fileUpload,
48739                 formData : this.form.formData
48740             }));
48741             
48742             this.uploadProgress();
48743
48744         }else if (o.clientValidation !== false){ // client validation failed
48745             this.failureType = Roo.form.Action.CLIENT_INVALID;
48746             this.form.afterAction(this, false);
48747         }
48748     },
48749
48750     success : function(response)
48751     {
48752         this.uploadComplete= true;
48753         if (this.haveProgress) {
48754             Roo.MessageBox.hide();
48755         }
48756         
48757         
48758         var result = this.processResponse(response);
48759         if(result === true || result.success){
48760             this.form.afterAction(this, true);
48761             return;
48762         }
48763         if(result.errors){
48764             this.form.markInvalid(result.errors);
48765             this.failureType = Roo.form.Action.SERVER_INVALID;
48766         }
48767         this.form.afterAction(this, false);
48768     },
48769     failure : function(response)
48770     {
48771         this.uploadComplete= true;
48772         if (this.haveProgress) {
48773             Roo.MessageBox.hide();
48774         }
48775         
48776         this.response = response;
48777         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48778         this.form.afterAction(this, false);
48779     },
48780     
48781     handleResponse : function(response){
48782         if(this.form.errorReader){
48783             var rs = this.form.errorReader.read(response);
48784             var errors = [];
48785             if(rs.records){
48786                 for(var i = 0, len = rs.records.length; i < len; i++) {
48787                     var r = rs.records[i];
48788                     errors[i] = r.data;
48789                 }
48790             }
48791             if(errors.length < 1){
48792                 errors = null;
48793             }
48794             return {
48795                 success : rs.success,
48796                 errors : errors
48797             };
48798         }
48799         var ret = false;
48800         try {
48801             ret = Roo.decode(response.responseText);
48802         } catch (e) {
48803             ret = {
48804                 success: false,
48805                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
48806                 errors : []
48807             };
48808         }
48809         return ret;
48810         
48811     }
48812 });
48813
48814
48815 Roo.form.Action.Load = function(form, options){
48816     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
48817     this.reader = this.form.reader;
48818 };
48819
48820 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
48821     type : 'load',
48822
48823     run : function(){
48824         
48825         Roo.Ajax.request(Roo.apply(
48826                 this.createCallback(), {
48827                     method:this.getMethod(),
48828                     url:this.getUrl(false),
48829                     params:this.getParams()
48830         }));
48831     },
48832
48833     success : function(response){
48834         
48835         var result = this.processResponse(response);
48836         if(result === true || !result.success || !result.data){
48837             this.failureType = Roo.form.Action.LOAD_FAILURE;
48838             this.form.afterAction(this, false);
48839             return;
48840         }
48841         this.form.clearInvalid();
48842         this.form.setValues(result.data);
48843         this.form.afterAction(this, true);
48844     },
48845
48846     handleResponse : function(response){
48847         if(this.form.reader){
48848             var rs = this.form.reader.read(response);
48849             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
48850             return {
48851                 success : rs.success,
48852                 data : data
48853             };
48854         }
48855         return Roo.decode(response.responseText);
48856     }
48857 });
48858
48859 Roo.form.Action.ACTION_TYPES = {
48860     'load' : Roo.form.Action.Load,
48861     'submit' : Roo.form.Action.Submit
48862 };/*
48863  * Based on:
48864  * Ext JS Library 1.1.1
48865  * Copyright(c) 2006-2007, Ext JS, LLC.
48866  *
48867  * Originally Released Under LGPL - original licence link has changed is not relivant.
48868  *
48869  * Fork - LGPL
48870  * <script type="text/javascript">
48871  */
48872  
48873 /**
48874  * @class Roo.form.Layout
48875  * @extends Roo.Component
48876  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48877  * @constructor
48878  * @param {Object} config Configuration options
48879  */
48880 Roo.form.Layout = function(config){
48881     var xitems = [];
48882     if (config.items) {
48883         xitems = config.items;
48884         delete config.items;
48885     }
48886     Roo.form.Layout.superclass.constructor.call(this, config);
48887     this.stack = [];
48888     Roo.each(xitems, this.addxtype, this);
48889      
48890 };
48891
48892 Roo.extend(Roo.form.Layout, Roo.Component, {
48893     /**
48894      * @cfg {String/Object} autoCreate
48895      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48896      */
48897     /**
48898      * @cfg {String/Object/Function} style
48899      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48900      * a function which returns such a specification.
48901      */
48902     /**
48903      * @cfg {String} labelAlign
48904      * Valid values are "left," "top" and "right" (defaults to "left")
48905      */
48906     /**
48907      * @cfg {Number} labelWidth
48908      * Fixed width in pixels of all field labels (defaults to undefined)
48909      */
48910     /**
48911      * @cfg {Boolean} clear
48912      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48913      */
48914     clear : true,
48915     /**
48916      * @cfg {String} labelSeparator
48917      * The separator to use after field labels (defaults to ':')
48918      */
48919     labelSeparator : ':',
48920     /**
48921      * @cfg {Boolean} hideLabels
48922      * True to suppress the display of field labels in this layout (defaults to false)
48923      */
48924     hideLabels : false,
48925
48926     // private
48927     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48928     
48929     isLayout : true,
48930     
48931     // private
48932     onRender : function(ct, position){
48933         if(this.el){ // from markup
48934             this.el = Roo.get(this.el);
48935         }else {  // generate
48936             var cfg = this.getAutoCreate();
48937             this.el = ct.createChild(cfg, position);
48938         }
48939         if(this.style){
48940             this.el.applyStyles(this.style);
48941         }
48942         if(this.labelAlign){
48943             this.el.addClass('x-form-label-'+this.labelAlign);
48944         }
48945         if(this.hideLabels){
48946             this.labelStyle = "display:none";
48947             this.elementStyle = "padding-left:0;";
48948         }else{
48949             if(typeof this.labelWidth == 'number'){
48950                 this.labelStyle = "width:"+this.labelWidth+"px;";
48951                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48952             }
48953             if(this.labelAlign == 'top'){
48954                 this.labelStyle = "width:auto;";
48955                 this.elementStyle = "padding-left:0;";
48956             }
48957         }
48958         var stack = this.stack;
48959         var slen = stack.length;
48960         if(slen > 0){
48961             if(!this.fieldTpl){
48962                 var t = new Roo.Template(
48963                     '<div class="x-form-item {5}">',
48964                         '<label for="{0}" style="{2}">{1}{4}</label>',
48965                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48966                         '</div>',
48967                     '</div><div class="x-form-clear-left"></div>'
48968                 );
48969                 t.disableFormats = true;
48970                 t.compile();
48971                 Roo.form.Layout.prototype.fieldTpl = t;
48972             }
48973             for(var i = 0; i < slen; i++) {
48974                 if(stack[i].isFormField){
48975                     this.renderField(stack[i]);
48976                 }else{
48977                     this.renderComponent(stack[i]);
48978                 }
48979             }
48980         }
48981         if(this.clear){
48982             this.el.createChild({cls:'x-form-clear'});
48983         }
48984     },
48985
48986     // private
48987     renderField : function(f){
48988         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48989                f.id, //0
48990                f.fieldLabel, //1
48991                f.labelStyle||this.labelStyle||'', //2
48992                this.elementStyle||'', //3
48993                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48994                f.itemCls||this.itemCls||''  //5
48995        ], true).getPrevSibling());
48996     },
48997
48998     // private
48999     renderComponent : function(c){
49000         c.render(c.isLayout ? this.el : this.el.createChild());    
49001     },
49002     /**
49003      * Adds a object form elements (using the xtype property as the factory method.)
49004      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49005      * @param {Object} config 
49006      */
49007     addxtype : function(o)
49008     {
49009         // create the lement.
49010         o.form = this.form;
49011         var fe = Roo.factory(o, Roo.form);
49012         this.form.allItems.push(fe);
49013         this.stack.push(fe);
49014         
49015         if (fe.isFormField) {
49016             this.form.items.add(fe);
49017         }
49018          
49019         return fe;
49020     }
49021 });
49022
49023 /**
49024  * @class Roo.form.Column
49025  * @extends Roo.form.Layout
49026  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49027  * @constructor
49028  * @param {Object} config Configuration options
49029  */
49030 Roo.form.Column = function(config){
49031     Roo.form.Column.superclass.constructor.call(this, config);
49032 };
49033
49034 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49035     /**
49036      * @cfg {Number/String} width
49037      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49038      */
49039     /**
49040      * @cfg {String/Object} autoCreate
49041      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49042      */
49043
49044     // private
49045     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49046
49047     // private
49048     onRender : function(ct, position){
49049         Roo.form.Column.superclass.onRender.call(this, ct, position);
49050         if(this.width){
49051             this.el.setWidth(this.width);
49052         }
49053     }
49054 });
49055
49056
49057 /**
49058  * @class Roo.form.Row
49059  * @extends Roo.form.Layout
49060  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49061  * @constructor
49062  * @param {Object} config Configuration options
49063  */
49064
49065  
49066 Roo.form.Row = function(config){
49067     Roo.form.Row.superclass.constructor.call(this, config);
49068 };
49069  
49070 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49071       /**
49072      * @cfg {Number/String} width
49073      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49074      */
49075     /**
49076      * @cfg {Number/String} height
49077      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49078      */
49079     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49080     
49081     padWidth : 20,
49082     // private
49083     onRender : function(ct, position){
49084         //console.log('row render');
49085         if(!this.rowTpl){
49086             var t = new Roo.Template(
49087                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49088                     '<label for="{0}" style="{2}">{1}{4}</label>',
49089                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49090                     '</div>',
49091                 '</div>'
49092             );
49093             t.disableFormats = true;
49094             t.compile();
49095             Roo.form.Layout.prototype.rowTpl = t;
49096         }
49097         this.fieldTpl = this.rowTpl;
49098         
49099         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49100         var labelWidth = 100;
49101         
49102         if ((this.labelAlign != 'top')) {
49103             if (typeof this.labelWidth == 'number') {
49104                 labelWidth = this.labelWidth
49105             }
49106             this.padWidth =  20 + labelWidth;
49107             
49108         }
49109         
49110         Roo.form.Column.superclass.onRender.call(this, ct, position);
49111         if(this.width){
49112             this.el.setWidth(this.width);
49113         }
49114         if(this.height){
49115             this.el.setHeight(this.height);
49116         }
49117     },
49118     
49119     // private
49120     renderField : function(f){
49121         f.fieldEl = this.fieldTpl.append(this.el, [
49122                f.id, f.fieldLabel,
49123                f.labelStyle||this.labelStyle||'',
49124                this.elementStyle||'',
49125                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49126                f.itemCls||this.itemCls||'',
49127                f.width ? f.width + this.padWidth : 160 + this.padWidth
49128        ],true);
49129     }
49130 });
49131  
49132
49133 /**
49134  * @class Roo.form.FieldSet
49135  * @extends Roo.form.Layout
49136  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49137  * @constructor
49138  * @param {Object} config Configuration options
49139  */
49140 Roo.form.FieldSet = function(config){
49141     Roo.form.FieldSet.superclass.constructor.call(this, config);
49142 };
49143
49144 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49145     /**
49146      * @cfg {String} legend
49147      * The text to display as the legend for the FieldSet (defaults to '')
49148      */
49149     /**
49150      * @cfg {String/Object} autoCreate
49151      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49152      */
49153
49154     // private
49155     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49156
49157     // private
49158     onRender : function(ct, position){
49159         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49160         if(this.legend){
49161             this.setLegend(this.legend);
49162         }
49163     },
49164
49165     // private
49166     setLegend : function(text){
49167         if(this.rendered){
49168             this.el.child('legend').update(text);
49169         }
49170     }
49171 });/*
49172  * Based on:
49173  * Ext JS Library 1.1.1
49174  * Copyright(c) 2006-2007, Ext JS, LLC.
49175  *
49176  * Originally Released Under LGPL - original licence link has changed is not relivant.
49177  *
49178  * Fork - LGPL
49179  * <script type="text/javascript">
49180  */
49181 /**
49182  * @class Roo.form.VTypes
49183  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49184  * @singleton
49185  */
49186 Roo.form.VTypes = function(){
49187     // closure these in so they are only created once.
49188     var alpha = /^[a-zA-Z_]+$/;
49189     var alphanum = /^[a-zA-Z0-9_]+$/;
49190     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49191     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49192
49193     // All these messages and functions are configurable
49194     return {
49195         /**
49196          * The function used to validate email addresses
49197          * @param {String} value The email address
49198          */
49199         'email' : function(v){
49200             return email.test(v);
49201         },
49202         /**
49203          * The error text to display when the email validation function returns false
49204          * @type String
49205          */
49206         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49207         /**
49208          * The keystroke filter mask to be applied on email input
49209          * @type RegExp
49210          */
49211         'emailMask' : /[a-z0-9_\.\-@]/i,
49212
49213         /**
49214          * The function used to validate URLs
49215          * @param {String} value The URL
49216          */
49217         'url' : function(v){
49218             return url.test(v);
49219         },
49220         /**
49221          * The error text to display when the url validation function returns false
49222          * @type String
49223          */
49224         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49225         
49226         /**
49227          * The function used to validate alpha values
49228          * @param {String} value The value
49229          */
49230         'alpha' : function(v){
49231             return alpha.test(v);
49232         },
49233         /**
49234          * The error text to display when the alpha validation function returns false
49235          * @type String
49236          */
49237         'alphaText' : 'This field should only contain letters and _',
49238         /**
49239          * The keystroke filter mask to be applied on alpha input
49240          * @type RegExp
49241          */
49242         'alphaMask' : /[a-z_]/i,
49243
49244         /**
49245          * The function used to validate alphanumeric values
49246          * @param {String} value The value
49247          */
49248         'alphanum' : function(v){
49249             return alphanum.test(v);
49250         },
49251         /**
49252          * The error text to display when the alphanumeric validation function returns false
49253          * @type String
49254          */
49255         'alphanumText' : 'This field should only contain letters, numbers and _',
49256         /**
49257          * The keystroke filter mask to be applied on alphanumeric input
49258          * @type RegExp
49259          */
49260         'alphanumMask' : /[a-z0-9_]/i
49261     };
49262 }();//<script type="text/javascript">
49263
49264 /**
49265  * @class Roo.form.FCKeditor
49266  * @extends Roo.form.TextArea
49267  * Wrapper around the FCKEditor http://www.fckeditor.net
49268  * @constructor
49269  * Creates a new FCKeditor
49270  * @param {Object} config Configuration options
49271  */
49272 Roo.form.FCKeditor = function(config){
49273     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49274     this.addEvents({
49275          /**
49276          * @event editorinit
49277          * Fired when the editor is initialized - you can add extra handlers here..
49278          * @param {FCKeditor} this
49279          * @param {Object} the FCK object.
49280          */
49281         editorinit : true
49282     });
49283     
49284     
49285 };
49286 Roo.form.FCKeditor.editors = { };
49287 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49288 {
49289     //defaultAutoCreate : {
49290     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49291     //},
49292     // private
49293     /**
49294      * @cfg {Object} fck options - see fck manual for details.
49295      */
49296     fckconfig : false,
49297     
49298     /**
49299      * @cfg {Object} fck toolbar set (Basic or Default)
49300      */
49301     toolbarSet : 'Basic',
49302     /**
49303      * @cfg {Object} fck BasePath
49304      */ 
49305     basePath : '/fckeditor/',
49306     
49307     
49308     frame : false,
49309     
49310     value : '',
49311     
49312    
49313     onRender : function(ct, position)
49314     {
49315         if(!this.el){
49316             this.defaultAutoCreate = {
49317                 tag: "textarea",
49318                 style:"width:300px;height:60px;",
49319                 autocomplete: "new-password"
49320             };
49321         }
49322         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49323         /*
49324         if(this.grow){
49325             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49326             if(this.preventScrollbars){
49327                 this.el.setStyle("overflow", "hidden");
49328             }
49329             this.el.setHeight(this.growMin);
49330         }
49331         */
49332         //console.log('onrender' + this.getId() );
49333         Roo.form.FCKeditor.editors[this.getId()] = this;
49334          
49335
49336         this.replaceTextarea() ;
49337         
49338     },
49339     
49340     getEditor : function() {
49341         return this.fckEditor;
49342     },
49343     /**
49344      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49345      * @param {Mixed} value The value to set
49346      */
49347     
49348     
49349     setValue : function(value)
49350     {
49351         //console.log('setValue: ' + value);
49352         
49353         if(typeof(value) == 'undefined') { // not sure why this is happending...
49354             return;
49355         }
49356         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49357         
49358         //if(!this.el || !this.getEditor()) {
49359         //    this.value = value;
49360             //this.setValue.defer(100,this,[value]);    
49361         //    return;
49362         //} 
49363         
49364         if(!this.getEditor()) {
49365             return;
49366         }
49367         
49368         this.getEditor().SetData(value);
49369         
49370         //
49371
49372     },
49373
49374     /**
49375      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49376      * @return {Mixed} value The field value
49377      */
49378     getValue : function()
49379     {
49380         
49381         if (this.frame && this.frame.dom.style.display == 'none') {
49382             return Roo.form.FCKeditor.superclass.getValue.call(this);
49383         }
49384         
49385         if(!this.el || !this.getEditor()) {
49386            
49387            // this.getValue.defer(100,this); 
49388             return this.value;
49389         }
49390        
49391         
49392         var value=this.getEditor().GetData();
49393         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49394         return Roo.form.FCKeditor.superclass.getValue.call(this);
49395         
49396
49397     },
49398
49399     /**
49400      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49401      * @return {Mixed} value The field value
49402      */
49403     getRawValue : function()
49404     {
49405         if (this.frame && this.frame.dom.style.display == 'none') {
49406             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49407         }
49408         
49409         if(!this.el || !this.getEditor()) {
49410             //this.getRawValue.defer(100,this); 
49411             return this.value;
49412             return;
49413         }
49414         
49415         
49416         
49417         var value=this.getEditor().GetData();
49418         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49419         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49420          
49421     },
49422     
49423     setSize : function(w,h) {
49424         
49425         
49426         
49427         //if (this.frame && this.frame.dom.style.display == 'none') {
49428         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49429         //    return;
49430         //}
49431         //if(!this.el || !this.getEditor()) {
49432         //    this.setSize.defer(100,this, [w,h]); 
49433         //    return;
49434         //}
49435         
49436         
49437         
49438         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49439         
49440         this.frame.dom.setAttribute('width', w);
49441         this.frame.dom.setAttribute('height', h);
49442         this.frame.setSize(w,h);
49443         
49444     },
49445     
49446     toggleSourceEdit : function(value) {
49447         
49448       
49449          
49450         this.el.dom.style.display = value ? '' : 'none';
49451         this.frame.dom.style.display = value ?  'none' : '';
49452         
49453     },
49454     
49455     
49456     focus: function(tag)
49457     {
49458         if (this.frame.dom.style.display == 'none') {
49459             return Roo.form.FCKeditor.superclass.focus.call(this);
49460         }
49461         if(!this.el || !this.getEditor()) {
49462             this.focus.defer(100,this, [tag]); 
49463             return;
49464         }
49465         
49466         
49467         
49468         
49469         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49470         this.getEditor().Focus();
49471         if (tgs.length) {
49472             if (!this.getEditor().Selection.GetSelection()) {
49473                 this.focus.defer(100,this, [tag]); 
49474                 return;
49475             }
49476             
49477             
49478             var r = this.getEditor().EditorDocument.createRange();
49479             r.setStart(tgs[0],0);
49480             r.setEnd(tgs[0],0);
49481             this.getEditor().Selection.GetSelection().removeAllRanges();
49482             this.getEditor().Selection.GetSelection().addRange(r);
49483             this.getEditor().Focus();
49484         }
49485         
49486     },
49487     
49488     
49489     
49490     replaceTextarea : function()
49491     {
49492         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49493             return ;
49494         }
49495         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49496         //{
49497             // We must check the elements firstly using the Id and then the name.
49498         var oTextarea = document.getElementById( this.getId() );
49499         
49500         var colElementsByName = document.getElementsByName( this.getId() ) ;
49501          
49502         oTextarea.style.display = 'none' ;
49503
49504         if ( oTextarea.tabIndex ) {            
49505             this.TabIndex = oTextarea.tabIndex ;
49506         }
49507         
49508         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49509         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49510         this.frame = Roo.get(this.getId() + '___Frame')
49511     },
49512     
49513     _getConfigHtml : function()
49514     {
49515         var sConfig = '' ;
49516
49517         for ( var o in this.fckconfig ) {
49518             sConfig += sConfig.length > 0  ? '&amp;' : '';
49519             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49520         }
49521
49522         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49523     },
49524     
49525     
49526     _getIFrameHtml : function()
49527     {
49528         var sFile = 'fckeditor.html' ;
49529         /* no idea what this is about..
49530         try
49531         {
49532             if ( (/fcksource=true/i).test( window.top.location.search ) )
49533                 sFile = 'fckeditor.original.html' ;
49534         }
49535         catch (e) { 
49536         */
49537
49538         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49539         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49540         
49541         
49542         var html = '<iframe id="' + this.getId() +
49543             '___Frame" src="' + sLink +
49544             '" width="' + this.width +
49545             '" height="' + this.height + '"' +
49546             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49547             ' frameborder="0" scrolling="no"></iframe>' ;
49548
49549         return html ;
49550     },
49551     
49552     _insertHtmlBefore : function( html, element )
49553     {
49554         if ( element.insertAdjacentHTML )       {
49555             // IE
49556             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49557         } else { // Gecko
49558             var oRange = document.createRange() ;
49559             oRange.setStartBefore( element ) ;
49560             var oFragment = oRange.createContextualFragment( html );
49561             element.parentNode.insertBefore( oFragment, element ) ;
49562         }
49563     }
49564     
49565     
49566   
49567     
49568     
49569     
49570     
49571
49572 });
49573
49574 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49575
49576 function FCKeditor_OnComplete(editorInstance){
49577     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49578     f.fckEditor = editorInstance;
49579     //console.log("loaded");
49580     f.fireEvent('editorinit', f, editorInstance);
49581
49582   
49583
49584  
49585
49586
49587
49588
49589
49590
49591
49592
49593
49594
49595
49596
49597
49598
49599
49600 //<script type="text/javascript">
49601 /**
49602  * @class Roo.form.GridField
49603  * @extends Roo.form.Field
49604  * Embed a grid (or editable grid into a form)
49605  * STATUS ALPHA
49606  * 
49607  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49608  * it needs 
49609  * xgrid.store = Roo.data.Store
49610  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49611  * xgrid.store.reader = Roo.data.JsonReader 
49612  * 
49613  * 
49614  * @constructor
49615  * Creates a new GridField
49616  * @param {Object} config Configuration options
49617  */
49618 Roo.form.GridField = function(config){
49619     Roo.form.GridField.superclass.constructor.call(this, config);
49620      
49621 };
49622
49623 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49624     /**
49625      * @cfg {Number} width  - used to restrict width of grid..
49626      */
49627     width : 100,
49628     /**
49629      * @cfg {Number} height - used to restrict height of grid..
49630      */
49631     height : 50,
49632      /**
49633      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49634          * 
49635          *}
49636      */
49637     xgrid : false, 
49638     /**
49639      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49640      * {tag: "input", type: "checkbox", autocomplete: "off"})
49641      */
49642    // defaultAutoCreate : { tag: 'div' },
49643     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
49644     /**
49645      * @cfg {String} addTitle Text to include for adding a title.
49646      */
49647     addTitle : false,
49648     //
49649     onResize : function(){
49650         Roo.form.Field.superclass.onResize.apply(this, arguments);
49651     },
49652
49653     initEvents : function(){
49654         // Roo.form.Checkbox.superclass.initEvents.call(this);
49655         // has no events...
49656        
49657     },
49658
49659
49660     getResizeEl : function(){
49661         return this.wrap;
49662     },
49663
49664     getPositionEl : function(){
49665         return this.wrap;
49666     },
49667
49668     // private
49669     onRender : function(ct, position){
49670         
49671         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
49672         var style = this.style;
49673         delete this.style;
49674         
49675         Roo.form.GridField.superclass.onRender.call(this, ct, position);
49676         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
49677         this.viewEl = this.wrap.createChild({ tag: 'div' });
49678         if (style) {
49679             this.viewEl.applyStyles(style);
49680         }
49681         if (this.width) {
49682             this.viewEl.setWidth(this.width);
49683         }
49684         if (this.height) {
49685             this.viewEl.setHeight(this.height);
49686         }
49687         //if(this.inputValue !== undefined){
49688         //this.setValue(this.value);
49689         
49690         
49691         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
49692         
49693         
49694         this.grid.render();
49695         this.grid.getDataSource().on('remove', this.refreshValue, this);
49696         this.grid.getDataSource().on('update', this.refreshValue, this);
49697         this.grid.on('afteredit', this.refreshValue, this);
49698  
49699     },
49700      
49701     
49702     /**
49703      * Sets the value of the item. 
49704      * @param {String} either an object  or a string..
49705      */
49706     setValue : function(v){
49707         //this.value = v;
49708         v = v || []; // empty set..
49709         // this does not seem smart - it really only affects memoryproxy grids..
49710         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
49711             var ds = this.grid.getDataSource();
49712             // assumes a json reader..
49713             var data = {}
49714             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
49715             ds.loadData( data);
49716         }
49717         // clear selection so it does not get stale.
49718         if (this.grid.sm) { 
49719             this.grid.sm.clearSelections();
49720         }
49721         
49722         Roo.form.GridField.superclass.setValue.call(this, v);
49723         this.refreshValue();
49724         // should load data in the grid really....
49725     },
49726     
49727     // private
49728     refreshValue: function() {
49729          var val = [];
49730         this.grid.getDataSource().each(function(r) {
49731             val.push(r.data);
49732         });
49733         this.el.dom.value = Roo.encode(val);
49734     }
49735     
49736      
49737     
49738     
49739 });/*
49740  * Based on:
49741  * Ext JS Library 1.1.1
49742  * Copyright(c) 2006-2007, Ext JS, LLC.
49743  *
49744  * Originally Released Under LGPL - original licence link has changed is not relivant.
49745  *
49746  * Fork - LGPL
49747  * <script type="text/javascript">
49748  */
49749 /**
49750  * @class Roo.form.DisplayField
49751  * @extends Roo.form.Field
49752  * A generic Field to display non-editable data.
49753  * @cfg {Boolean} closable (true|false) default false
49754  * @constructor
49755  * Creates a new Display Field item.
49756  * @param {Object} config Configuration options
49757  */
49758 Roo.form.DisplayField = function(config){
49759     Roo.form.DisplayField.superclass.constructor.call(this, config);
49760     
49761     this.addEvents({
49762         /**
49763          * @event close
49764          * Fires after the click the close btn
49765              * @param {Roo.form.DisplayField} this
49766              */
49767         close : true
49768     });
49769 };
49770
49771 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
49772     inputType:      'hidden',
49773     allowBlank:     true,
49774     readOnly:         true,
49775     
49776  
49777     /**
49778      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49779      */
49780     focusClass : undefined,
49781     /**
49782      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49783      */
49784     fieldClass: 'x-form-field',
49785     
49786      /**
49787      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
49788      */
49789     valueRenderer: undefined,
49790     
49791     width: 100,
49792     /**
49793      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49794      * {tag: "input", type: "checkbox", autocomplete: "off"})
49795      */
49796      
49797  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
49798  
49799     closable : false,
49800     
49801     onResize : function(){
49802         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
49803         
49804     },
49805
49806     initEvents : function(){
49807         // Roo.form.Checkbox.superclass.initEvents.call(this);
49808         // has no events...
49809         
49810         if(this.closable){
49811             this.closeEl.on('click', this.onClose, this);
49812         }
49813        
49814     },
49815
49816
49817     getResizeEl : function(){
49818         return this.wrap;
49819     },
49820
49821     getPositionEl : function(){
49822         return this.wrap;
49823     },
49824
49825     // private
49826     onRender : function(ct, position){
49827         
49828         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
49829         //if(this.inputValue !== undefined){
49830         this.wrap = this.el.wrap();
49831         
49832         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
49833         
49834         if(this.closable){
49835             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
49836         }
49837         
49838         if (this.bodyStyle) {
49839             this.viewEl.applyStyles(this.bodyStyle);
49840         }
49841         //this.viewEl.setStyle('padding', '2px');
49842         
49843         this.setValue(this.value);
49844         
49845     },
49846 /*
49847     // private
49848     initValue : Roo.emptyFn,
49849
49850   */
49851
49852         // private
49853     onClick : function(){
49854         
49855     },
49856
49857     /**
49858      * Sets the checked state of the checkbox.
49859      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
49860      */
49861     setValue : function(v){
49862         this.value = v;
49863         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
49864         // this might be called before we have a dom element..
49865         if (!this.viewEl) {
49866             return;
49867         }
49868         this.viewEl.dom.innerHTML = html;
49869         Roo.form.DisplayField.superclass.setValue.call(this, v);
49870
49871     },
49872     
49873     onClose : function(e)
49874     {
49875         e.preventDefault();
49876         
49877         this.fireEvent('close', this);
49878     }
49879 });/*
49880  * 
49881  * Licence- LGPL
49882  * 
49883  */
49884
49885 /**
49886  * @class Roo.form.DayPicker
49887  * @extends Roo.form.Field
49888  * A Day picker show [M] [T] [W] ....
49889  * @constructor
49890  * Creates a new Day Picker
49891  * @param {Object} config Configuration options
49892  */
49893 Roo.form.DayPicker= function(config){
49894     Roo.form.DayPicker.superclass.constructor.call(this, config);
49895      
49896 };
49897
49898 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49899     /**
49900      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49901      */
49902     focusClass : undefined,
49903     /**
49904      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49905      */
49906     fieldClass: "x-form-field",
49907    
49908     /**
49909      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49910      * {tag: "input", type: "checkbox", autocomplete: "off"})
49911      */
49912     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49913     
49914    
49915     actionMode : 'viewEl', 
49916     //
49917     // private
49918  
49919     inputType : 'hidden',
49920     
49921      
49922     inputElement: false, // real input element?
49923     basedOn: false, // ????
49924     
49925     isFormField: true, // not sure where this is needed!!!!
49926
49927     onResize : function(){
49928         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49929         if(!this.boxLabel){
49930             this.el.alignTo(this.wrap, 'c-c');
49931         }
49932     },
49933
49934     initEvents : function(){
49935         Roo.form.Checkbox.superclass.initEvents.call(this);
49936         this.el.on("click", this.onClick,  this);
49937         this.el.on("change", this.onClick,  this);
49938     },
49939
49940
49941     getResizeEl : function(){
49942         return this.wrap;
49943     },
49944
49945     getPositionEl : function(){
49946         return this.wrap;
49947     },
49948
49949     
49950     // private
49951     onRender : function(ct, position){
49952         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49953        
49954         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49955         
49956         var r1 = '<table><tr>';
49957         var r2 = '<tr class="x-form-daypick-icons">';
49958         for (var i=0; i < 7; i++) {
49959             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49960             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49961         }
49962         
49963         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49964         viewEl.select('img').on('click', this.onClick, this);
49965         this.viewEl = viewEl;   
49966         
49967         
49968         // this will not work on Chrome!!!
49969         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49970         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49971         
49972         
49973           
49974
49975     },
49976
49977     // private
49978     initValue : Roo.emptyFn,
49979
49980     /**
49981      * Returns the checked state of the checkbox.
49982      * @return {Boolean} True if checked, else false
49983      */
49984     getValue : function(){
49985         return this.el.dom.value;
49986         
49987     },
49988
49989         // private
49990     onClick : function(e){ 
49991         //this.setChecked(!this.checked);
49992         Roo.get(e.target).toggleClass('x-menu-item-checked');
49993         this.refreshValue();
49994         //if(this.el.dom.checked != this.checked){
49995         //    this.setValue(this.el.dom.checked);
49996        // }
49997     },
49998     
49999     // private
50000     refreshValue : function()
50001     {
50002         var val = '';
50003         this.viewEl.select('img',true).each(function(e,i,n)  {
50004             val += e.is(".x-menu-item-checked") ? String(n) : '';
50005         });
50006         this.setValue(val, true);
50007     },
50008
50009     /**
50010      * Sets the checked state of the checkbox.
50011      * On is always based on a string comparison between inputValue and the param.
50012      * @param {Boolean/String} value - the value to set 
50013      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50014      */
50015     setValue : function(v,suppressEvent){
50016         if (!this.el.dom) {
50017             return;
50018         }
50019         var old = this.el.dom.value ;
50020         this.el.dom.value = v;
50021         if (suppressEvent) {
50022             return ;
50023         }
50024          
50025         // update display..
50026         this.viewEl.select('img',true).each(function(e,i,n)  {
50027             
50028             var on = e.is(".x-menu-item-checked");
50029             var newv = v.indexOf(String(n)) > -1;
50030             if (on != newv) {
50031                 e.toggleClass('x-menu-item-checked');
50032             }
50033             
50034         });
50035         
50036         
50037         this.fireEvent('change', this, v, old);
50038         
50039         
50040     },
50041    
50042     // handle setting of hidden value by some other method!!?!?
50043     setFromHidden: function()
50044     {
50045         if(!this.el){
50046             return;
50047         }
50048         //console.log("SET FROM HIDDEN");
50049         //alert('setFrom hidden');
50050         this.setValue(this.el.dom.value);
50051     },
50052     
50053     onDestroy : function()
50054     {
50055         if(this.viewEl){
50056             Roo.get(this.viewEl).remove();
50057         }
50058          
50059         Roo.form.DayPicker.superclass.onDestroy.call(this);
50060     }
50061
50062 });/*
50063  * RooJS Library 1.1.1
50064  * Copyright(c) 2008-2011  Alan Knowles
50065  *
50066  * License - LGPL
50067  */
50068  
50069
50070 /**
50071  * @class Roo.form.ComboCheck
50072  * @extends Roo.form.ComboBox
50073  * A combobox for multiple select items.
50074  *
50075  * FIXME - could do with a reset button..
50076  * 
50077  * @constructor
50078  * Create a new ComboCheck
50079  * @param {Object} config Configuration options
50080  */
50081 Roo.form.ComboCheck = function(config){
50082     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50083     // should verify some data...
50084     // like
50085     // hiddenName = required..
50086     // displayField = required
50087     // valudField == required
50088     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50089     var _t = this;
50090     Roo.each(req, function(e) {
50091         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50092             throw "Roo.form.ComboCheck : missing value for: " + e;
50093         }
50094     });
50095     
50096     
50097 };
50098
50099 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50100      
50101      
50102     editable : false,
50103      
50104     selectedClass: 'x-menu-item-checked', 
50105     
50106     // private
50107     onRender : function(ct, position){
50108         var _t = this;
50109         
50110         
50111         
50112         if(!this.tpl){
50113             var cls = 'x-combo-list';
50114
50115             
50116             this.tpl =  new Roo.Template({
50117                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50118                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50119                    '<span>{' + this.displayField + '}</span>' +
50120                     '</div>' 
50121                 
50122             });
50123         }
50124  
50125         
50126         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50127         this.view.singleSelect = false;
50128         this.view.multiSelect = true;
50129         this.view.toggleSelect = true;
50130         this.pageTb.add(new Roo.Toolbar.Fill(), {
50131             
50132             text: 'Done',
50133             handler: function()
50134             {
50135                 _t.collapse();
50136             }
50137         });
50138     },
50139     
50140     onViewOver : function(e, t){
50141         // do nothing...
50142         return;
50143         
50144     },
50145     
50146     onViewClick : function(doFocus,index){
50147         return;
50148         
50149     },
50150     select: function () {
50151         //Roo.log("SELECT CALLED");
50152     },
50153      
50154     selectByValue : function(xv, scrollIntoView){
50155         var ar = this.getValueArray();
50156         var sels = [];
50157         
50158         Roo.each(ar, function(v) {
50159             if(v === undefined || v === null){
50160                 return;
50161             }
50162             var r = this.findRecord(this.valueField, v);
50163             if(r){
50164                 sels.push(this.store.indexOf(r))
50165                 
50166             }
50167         },this);
50168         this.view.select(sels);
50169         return false;
50170     },
50171     
50172     
50173     
50174     onSelect : function(record, index){
50175        // Roo.log("onselect Called");
50176        // this is only called by the clear button now..
50177         this.view.clearSelections();
50178         this.setValue('[]');
50179         if (this.value != this.valueBefore) {
50180             this.fireEvent('change', this, this.value, this.valueBefore);
50181             this.valueBefore = this.value;
50182         }
50183     },
50184     getValueArray : function()
50185     {
50186         var ar = [] ;
50187         
50188         try {
50189             //Roo.log(this.value);
50190             if (typeof(this.value) == 'undefined') {
50191                 return [];
50192             }
50193             var ar = Roo.decode(this.value);
50194             return  ar instanceof Array ? ar : []; //?? valid?
50195             
50196         } catch(e) {
50197             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50198             return [];
50199         }
50200          
50201     },
50202     expand : function ()
50203     {
50204         
50205         Roo.form.ComboCheck.superclass.expand.call(this);
50206         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50207         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50208         
50209
50210     },
50211     
50212     collapse : function(){
50213         Roo.form.ComboCheck.superclass.collapse.call(this);
50214         var sl = this.view.getSelectedIndexes();
50215         var st = this.store;
50216         var nv = [];
50217         var tv = [];
50218         var r;
50219         Roo.each(sl, function(i) {
50220             r = st.getAt(i);
50221             nv.push(r.get(this.valueField));
50222         },this);
50223         this.setValue(Roo.encode(nv));
50224         if (this.value != this.valueBefore) {
50225
50226             this.fireEvent('change', this, this.value, this.valueBefore);
50227             this.valueBefore = this.value;
50228         }
50229         
50230     },
50231     
50232     setValue : function(v){
50233         // Roo.log(v);
50234         this.value = v;
50235         
50236         var vals = this.getValueArray();
50237         var tv = [];
50238         Roo.each(vals, function(k) {
50239             var r = this.findRecord(this.valueField, k);
50240             if(r){
50241                 tv.push(r.data[this.displayField]);
50242             }else if(this.valueNotFoundText !== undefined){
50243                 tv.push( this.valueNotFoundText );
50244             }
50245         },this);
50246        // Roo.log(tv);
50247         
50248         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50249         this.hiddenField.value = v;
50250         this.value = v;
50251     }
50252     
50253 });/*
50254  * Based on:
50255  * Ext JS Library 1.1.1
50256  * Copyright(c) 2006-2007, Ext JS, LLC.
50257  *
50258  * Originally Released Under LGPL - original licence link has changed is not relivant.
50259  *
50260  * Fork - LGPL
50261  * <script type="text/javascript">
50262  */
50263  
50264 /**
50265  * @class Roo.form.Signature
50266  * @extends Roo.form.Field
50267  * Signature field.  
50268  * @constructor
50269  * 
50270  * @param {Object} config Configuration options
50271  */
50272
50273 Roo.form.Signature = function(config){
50274     Roo.form.Signature.superclass.constructor.call(this, config);
50275     
50276     this.addEvents({// not in used??
50277          /**
50278          * @event confirm
50279          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50280              * @param {Roo.form.Signature} combo This combo box
50281              */
50282         'confirm' : true,
50283         /**
50284          * @event reset
50285          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50286              * @param {Roo.form.ComboBox} combo This combo box
50287              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50288              */
50289         'reset' : true
50290     });
50291 };
50292
50293 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50294     /**
50295      * @cfg {Object} labels Label to use when rendering a form.
50296      * defaults to 
50297      * labels : { 
50298      *      clear : "Clear",
50299      *      confirm : "Confirm"
50300      *  }
50301      */
50302     labels : { 
50303         clear : "Clear",
50304         confirm : "Confirm"
50305     },
50306     /**
50307      * @cfg {Number} width The signature panel width (defaults to 300)
50308      */
50309     width: 300,
50310     /**
50311      * @cfg {Number} height The signature panel height (defaults to 100)
50312      */
50313     height : 100,
50314     /**
50315      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50316      */
50317     allowBlank : false,
50318     
50319     //private
50320     // {Object} signPanel The signature SVG panel element (defaults to {})
50321     signPanel : {},
50322     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50323     isMouseDown : false,
50324     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50325     isConfirmed : false,
50326     // {String} signatureTmp SVG mapping string (defaults to empty string)
50327     signatureTmp : '',
50328     
50329     
50330     defaultAutoCreate : { // modified by initCompnoent..
50331         tag: "input",
50332         type:"hidden"
50333     },
50334
50335     // private
50336     onRender : function(ct, position){
50337         
50338         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50339         
50340         this.wrap = this.el.wrap({
50341             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50342         });
50343         
50344         this.createToolbar(this);
50345         this.signPanel = this.wrap.createChild({
50346                 tag: 'div',
50347                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50348             }, this.el
50349         );
50350             
50351         this.svgID = Roo.id();
50352         this.svgEl = this.signPanel.createChild({
50353               xmlns : 'http://www.w3.org/2000/svg',
50354               tag : 'svg',
50355               id : this.svgID + "-svg",
50356               width: this.width,
50357               height: this.height,
50358               viewBox: '0 0 '+this.width+' '+this.height,
50359               cn : [
50360                 {
50361                     tag: "rect",
50362                     id: this.svgID + "-svg-r",
50363                     width: this.width,
50364                     height: this.height,
50365                     fill: "#ffa"
50366                 },
50367                 {
50368                     tag: "line",
50369                     id: this.svgID + "-svg-l",
50370                     x1: "0", // start
50371                     y1: (this.height*0.8), // start set the line in 80% of height
50372                     x2: this.width, // end
50373                     y2: (this.height*0.8), // end set the line in 80% of height
50374                     'stroke': "#666",
50375                     'stroke-width': "1",
50376                     'stroke-dasharray': "3",
50377                     'shape-rendering': "crispEdges",
50378                     'pointer-events': "none"
50379                 },
50380                 {
50381                     tag: "path",
50382                     id: this.svgID + "-svg-p",
50383                     'stroke': "navy",
50384                     'stroke-width': "3",
50385                     'fill': "none",
50386                     'pointer-events': 'none'
50387                 }
50388               ]
50389         });
50390         this.createSVG();
50391         this.svgBox = this.svgEl.dom.getScreenCTM();
50392     },
50393     createSVG : function(){ 
50394         var svg = this.signPanel;
50395         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50396         var t = this;
50397
50398         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50399         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50400         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50401         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50402         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50403         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50404         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50405         
50406     },
50407     isTouchEvent : function(e){
50408         return e.type.match(/^touch/);
50409     },
50410     getCoords : function (e) {
50411         var pt    = this.svgEl.dom.createSVGPoint();
50412         pt.x = e.clientX; 
50413         pt.y = e.clientY;
50414         if (this.isTouchEvent(e)) {
50415             pt.x =  e.targetTouches[0].clientX;
50416             pt.y = e.targetTouches[0].clientY;
50417         }
50418         var a = this.svgEl.dom.getScreenCTM();
50419         var b = a.inverse();
50420         var mx = pt.matrixTransform(b);
50421         return mx.x + ',' + mx.y;
50422     },
50423     //mouse event headler 
50424     down : function (e) {
50425         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50426         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50427         
50428         this.isMouseDown = true;
50429         
50430         e.preventDefault();
50431     },
50432     move : function (e) {
50433         if (this.isMouseDown) {
50434             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50435             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50436         }
50437         
50438         e.preventDefault();
50439     },
50440     up : function (e) {
50441         this.isMouseDown = false;
50442         var sp = this.signatureTmp.split(' ');
50443         
50444         if(sp.length > 1){
50445             if(!sp[sp.length-2].match(/^L/)){
50446                 sp.pop();
50447                 sp.pop();
50448                 sp.push("");
50449                 this.signatureTmp = sp.join(" ");
50450             }
50451         }
50452         if(this.getValue() != this.signatureTmp){
50453             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50454             this.isConfirmed = false;
50455         }
50456         e.preventDefault();
50457     },
50458     
50459     /**
50460      * Protected method that will not generally be called directly. It
50461      * is called when the editor creates its toolbar. Override this method if you need to
50462      * add custom toolbar buttons.
50463      * @param {HtmlEditor} editor
50464      */
50465     createToolbar : function(editor){
50466          function btn(id, toggle, handler){
50467             var xid = fid + '-'+ id ;
50468             return {
50469                 id : xid,
50470                 cmd : id,
50471                 cls : 'x-btn-icon x-edit-'+id,
50472                 enableToggle:toggle !== false,
50473                 scope: editor, // was editor...
50474                 handler:handler||editor.relayBtnCmd,
50475                 clickEvent:'mousedown',
50476                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50477                 tabIndex:-1
50478             };
50479         }
50480         
50481         
50482         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50483         this.tb = tb;
50484         this.tb.add(
50485            {
50486                 cls : ' x-signature-btn x-signature-'+id,
50487                 scope: editor, // was editor...
50488                 handler: this.reset,
50489                 clickEvent:'mousedown',
50490                 text: this.labels.clear
50491             },
50492             {
50493                  xtype : 'Fill',
50494                  xns: Roo.Toolbar
50495             }, 
50496             {
50497                 cls : '  x-signature-btn x-signature-'+id,
50498                 scope: editor, // was editor...
50499                 handler: this.confirmHandler,
50500                 clickEvent:'mousedown',
50501                 text: this.labels.confirm
50502             }
50503         );
50504     
50505     },
50506     //public
50507     /**
50508      * when user is clicked confirm then show this image.....
50509      * 
50510      * @return {String} Image Data URI
50511      */
50512     getImageDataURI : function(){
50513         var svg = this.svgEl.dom.parentNode.innerHTML;
50514         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50515         return src; 
50516     },
50517     /**
50518      * 
50519      * @return {Boolean} this.isConfirmed
50520      */
50521     getConfirmed : function(){
50522         return this.isConfirmed;
50523     },
50524     /**
50525      * 
50526      * @return {Number} this.width
50527      */
50528     getWidth : function(){
50529         return this.width;
50530     },
50531     /**
50532      * 
50533      * @return {Number} this.height
50534      */
50535     getHeight : function(){
50536         return this.height;
50537     },
50538     // private
50539     getSignature : function(){
50540         return this.signatureTmp;
50541     },
50542     // private
50543     reset : function(){
50544         this.signatureTmp = '';
50545         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50546         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50547         this.isConfirmed = false;
50548         Roo.form.Signature.superclass.reset.call(this);
50549     },
50550     setSignature : function(s){
50551         this.signatureTmp = s;
50552         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50553         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50554         this.setValue(s);
50555         this.isConfirmed = false;
50556         Roo.form.Signature.superclass.reset.call(this);
50557     }, 
50558     test : function(){
50559 //        Roo.log(this.signPanel.dom.contentWindow.up())
50560     },
50561     //private
50562     setConfirmed : function(){
50563         
50564         
50565         
50566 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50567     },
50568     // private
50569     confirmHandler : function(){
50570         if(!this.getSignature()){
50571             return;
50572         }
50573         
50574         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50575         this.setValue(this.getSignature());
50576         this.isConfirmed = true;
50577         
50578         this.fireEvent('confirm', this);
50579     },
50580     // private
50581     // Subclasses should provide the validation implementation by overriding this
50582     validateValue : function(value){
50583         if(this.allowBlank){
50584             return true;
50585         }
50586         
50587         if(this.isConfirmed){
50588             return true;
50589         }
50590         return false;
50591     }
50592 });/*
50593  * Based on:
50594  * Ext JS Library 1.1.1
50595  * Copyright(c) 2006-2007, Ext JS, LLC.
50596  *
50597  * Originally Released Under LGPL - original licence link has changed is not relivant.
50598  *
50599  * Fork - LGPL
50600  * <script type="text/javascript">
50601  */
50602  
50603
50604 /**
50605  * @class Roo.form.ComboBox
50606  * @extends Roo.form.TriggerField
50607  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50608  * @constructor
50609  * Create a new ComboBox.
50610  * @param {Object} config Configuration options
50611  */
50612 Roo.form.Select = function(config){
50613     Roo.form.Select.superclass.constructor.call(this, config);
50614      
50615 };
50616
50617 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50618     /**
50619      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50620      */
50621     /**
50622      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50623      * rendering into an Roo.Editor, defaults to false)
50624      */
50625     /**
50626      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50627      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50628      */
50629     /**
50630      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50631      */
50632     /**
50633      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50634      * the dropdown list (defaults to undefined, with no header element)
50635      */
50636
50637      /**
50638      * @cfg {String/Roo.Template} tpl The template to use to render the output
50639      */
50640      
50641     // private
50642     defaultAutoCreate : {tag: "select"  },
50643     /**
50644      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
50645      */
50646     listWidth: undefined,
50647     /**
50648      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
50649      * mode = 'remote' or 'text' if mode = 'local')
50650      */
50651     displayField: undefined,
50652     /**
50653      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
50654      * mode = 'remote' or 'value' if mode = 'local'). 
50655      * Note: use of a valueField requires the user make a selection
50656      * in order for a value to be mapped.
50657      */
50658     valueField: undefined,
50659     
50660     
50661     /**
50662      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
50663      * field's data value (defaults to the underlying DOM element's name)
50664      */
50665     hiddenName: undefined,
50666     /**
50667      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
50668      */
50669     listClass: '',
50670     /**
50671      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
50672      */
50673     selectedClass: 'x-combo-selected',
50674     /**
50675      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
50676      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
50677      * which displays a downward arrow icon).
50678      */
50679     triggerClass : 'x-form-arrow-trigger',
50680     /**
50681      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
50682      */
50683     shadow:'sides',
50684     /**
50685      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
50686      * anchor positions (defaults to 'tl-bl')
50687      */
50688     listAlign: 'tl-bl?',
50689     /**
50690      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
50691      */
50692     maxHeight: 300,
50693     /**
50694      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
50695      * query specified by the allQuery config option (defaults to 'query')
50696      */
50697     triggerAction: 'query',
50698     /**
50699      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
50700      * (defaults to 4, does not apply if editable = false)
50701      */
50702     minChars : 4,
50703     /**
50704      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
50705      * delay (typeAheadDelay) if it matches a known value (defaults to false)
50706      */
50707     typeAhead: false,
50708     /**
50709      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
50710      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
50711      */
50712     queryDelay: 500,
50713     /**
50714      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
50715      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
50716      */
50717     pageSize: 0,
50718     /**
50719      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
50720      * when editable = true (defaults to false)
50721      */
50722     selectOnFocus:false,
50723     /**
50724      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
50725      */
50726     queryParam: 'query',
50727     /**
50728      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
50729      * when mode = 'remote' (defaults to 'Loading...')
50730      */
50731     loadingText: 'Loading...',
50732     /**
50733      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
50734      */
50735     resizable: false,
50736     /**
50737      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
50738      */
50739     handleHeight : 8,
50740     /**
50741      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
50742      * traditional select (defaults to true)
50743      */
50744     editable: true,
50745     /**
50746      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
50747      */
50748     allQuery: '',
50749     /**
50750      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
50751      */
50752     mode: 'remote',
50753     /**
50754      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
50755      * listWidth has a higher value)
50756      */
50757     minListWidth : 70,
50758     /**
50759      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
50760      * allow the user to set arbitrary text into the field (defaults to false)
50761      */
50762     forceSelection:false,
50763     /**
50764      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
50765      * if typeAhead = true (defaults to 250)
50766      */
50767     typeAheadDelay : 250,
50768     /**
50769      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50770      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
50771      */
50772     valueNotFoundText : undefined,
50773     
50774     /**
50775      * @cfg {String} defaultValue The value displayed after loading the store.
50776      */
50777     defaultValue: '',
50778     
50779     /**
50780      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
50781      */
50782     blockFocus : false,
50783     
50784     /**
50785      * @cfg {Boolean} disableClear Disable showing of clear button.
50786      */
50787     disableClear : false,
50788     /**
50789      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
50790      */
50791     alwaysQuery : false,
50792     
50793     //private
50794     addicon : false,
50795     editicon: false,
50796     
50797     // element that contains real text value.. (when hidden is used..)
50798      
50799     // private
50800     onRender : function(ct, position){
50801         Roo.form.Field.prototype.onRender.call(this, ct, position);
50802         
50803         if(this.store){
50804             this.store.on('beforeload', this.onBeforeLoad, this);
50805             this.store.on('load', this.onLoad, this);
50806             this.store.on('loadexception', this.onLoadException, this);
50807             this.store.load({});
50808         }
50809         
50810         
50811         
50812     },
50813
50814     // private
50815     initEvents : function(){
50816         //Roo.form.ComboBox.superclass.initEvents.call(this);
50817  
50818     },
50819
50820     onDestroy : function(){
50821        
50822         if(this.store){
50823             this.store.un('beforeload', this.onBeforeLoad, this);
50824             this.store.un('load', this.onLoad, this);
50825             this.store.un('loadexception', this.onLoadException, this);
50826         }
50827         //Roo.form.ComboBox.superclass.onDestroy.call(this);
50828     },
50829
50830     // private
50831     fireKey : function(e){
50832         if(e.isNavKeyPress() && !this.list.isVisible()){
50833             this.fireEvent("specialkey", this, e);
50834         }
50835     },
50836
50837     // private
50838     onResize: function(w, h){
50839         
50840         return; 
50841     
50842         
50843     },
50844
50845     /**
50846      * Allow or prevent the user from directly editing the field text.  If false is passed,
50847      * the user will only be able to select from the items defined in the dropdown list.  This method
50848      * is the runtime equivalent of setting the 'editable' config option at config time.
50849      * @param {Boolean} value True to allow the user to directly edit the field text
50850      */
50851     setEditable : function(value){
50852          
50853     },
50854
50855     // private
50856     onBeforeLoad : function(){
50857         
50858         Roo.log("Select before load");
50859         return;
50860     
50861         this.innerList.update(this.loadingText ?
50862                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
50863         //this.restrictHeight();
50864         this.selectedIndex = -1;
50865     },
50866
50867     // private
50868     onLoad : function(){
50869
50870     
50871         var dom = this.el.dom;
50872         dom.innerHTML = '';
50873          var od = dom.ownerDocument;
50874          
50875         if (this.emptyText) {
50876             var op = od.createElement('option');
50877             op.setAttribute('value', '');
50878             op.innerHTML = String.format('{0}', this.emptyText);
50879             dom.appendChild(op);
50880         }
50881         if(this.store.getCount() > 0){
50882            
50883             var vf = this.valueField;
50884             var df = this.displayField;
50885             this.store.data.each(function(r) {
50886                 // which colmsn to use... testing - cdoe / title..
50887                 var op = od.createElement('option');
50888                 op.setAttribute('value', r.data[vf]);
50889                 op.innerHTML = String.format('{0}', r.data[df]);
50890                 dom.appendChild(op);
50891             });
50892             if (typeof(this.defaultValue != 'undefined')) {
50893                 this.setValue(this.defaultValue);
50894             }
50895             
50896              
50897         }else{
50898             //this.onEmptyResults();
50899         }
50900         //this.el.focus();
50901     },
50902     // private
50903     onLoadException : function()
50904     {
50905         dom.innerHTML = '';
50906             
50907         Roo.log("Select on load exception");
50908         return;
50909     
50910         this.collapse();
50911         Roo.log(this.store.reader.jsonData);
50912         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50913             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50914         }
50915         
50916         
50917     },
50918     // private
50919     onTypeAhead : function(){
50920          
50921     },
50922
50923     // private
50924     onSelect : function(record, index){
50925         Roo.log('on select?');
50926         return;
50927         if(this.fireEvent('beforeselect', this, record, index) !== false){
50928             this.setFromData(index > -1 ? record.data : false);
50929             this.collapse();
50930             this.fireEvent('select', this, record, index);
50931         }
50932     },
50933
50934     /**
50935      * Returns the currently selected field value or empty string if no value is set.
50936      * @return {String} value The selected value
50937      */
50938     getValue : function(){
50939         var dom = this.el.dom;
50940         this.value = dom.options[dom.selectedIndex].value;
50941         return this.value;
50942         
50943     },
50944
50945     /**
50946      * Clears any text/value currently set in the field
50947      */
50948     clearValue : function(){
50949         this.value = '';
50950         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50951         
50952     },
50953
50954     /**
50955      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50956      * will be displayed in the field.  If the value does not match the data value of an existing item,
50957      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50958      * Otherwise the field will be blank (although the value will still be set).
50959      * @param {String} value The value to match
50960      */
50961     setValue : function(v){
50962         var d = this.el.dom;
50963         for (var i =0; i < d.options.length;i++) {
50964             if (v == d.options[i].value) {
50965                 d.selectedIndex = i;
50966                 this.value = v;
50967                 return;
50968             }
50969         }
50970         this.clearValue();
50971     },
50972     /**
50973      * @property {Object} the last set data for the element
50974      */
50975     
50976     lastData : false,
50977     /**
50978      * Sets the value of the field based on a object which is related to the record format for the store.
50979      * @param {Object} value the value to set as. or false on reset?
50980      */
50981     setFromData : function(o){
50982         Roo.log('setfrom data?');
50983          
50984         
50985         
50986     },
50987     // private
50988     reset : function(){
50989         this.clearValue();
50990     },
50991     // private
50992     findRecord : function(prop, value){
50993         
50994         return false;
50995     
50996         var record;
50997         if(this.store.getCount() > 0){
50998             this.store.each(function(r){
50999                 if(r.data[prop] == value){
51000                     record = r;
51001                     return false;
51002                 }
51003                 return true;
51004             });
51005         }
51006         return record;
51007     },
51008     
51009     getName: function()
51010     {
51011         // returns hidden if it's set..
51012         if (!this.rendered) {return ''};
51013         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51014         
51015     },
51016      
51017
51018     
51019
51020     // private
51021     onEmptyResults : function(){
51022         Roo.log('empty results');
51023         //this.collapse();
51024     },
51025
51026     /**
51027      * Returns true if the dropdown list is expanded, else false.
51028      */
51029     isExpanded : function(){
51030         return false;
51031     },
51032
51033     /**
51034      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51035      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51036      * @param {String} value The data value of the item to select
51037      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51038      * selected item if it is not currently in view (defaults to true)
51039      * @return {Boolean} True if the value matched an item in the list, else false
51040      */
51041     selectByValue : function(v, scrollIntoView){
51042         Roo.log('select By Value');
51043         return false;
51044     
51045         if(v !== undefined && v !== null){
51046             var r = this.findRecord(this.valueField || this.displayField, v);
51047             if(r){
51048                 this.select(this.store.indexOf(r), scrollIntoView);
51049                 return true;
51050             }
51051         }
51052         return false;
51053     },
51054
51055     /**
51056      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51057      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51058      * @param {Number} index The zero-based index of the list item to select
51059      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51060      * selected item if it is not currently in view (defaults to true)
51061      */
51062     select : function(index, scrollIntoView){
51063         Roo.log('select ');
51064         return  ;
51065         
51066         this.selectedIndex = index;
51067         this.view.select(index);
51068         if(scrollIntoView !== false){
51069             var el = this.view.getNode(index);
51070             if(el){
51071                 this.innerList.scrollChildIntoView(el, false);
51072             }
51073         }
51074     },
51075
51076       
51077
51078     // private
51079     validateBlur : function(){
51080         
51081         return;
51082         
51083     },
51084
51085     // private
51086     initQuery : function(){
51087         this.doQuery(this.getRawValue());
51088     },
51089
51090     // private
51091     doForce : function(){
51092         if(this.el.dom.value.length > 0){
51093             this.el.dom.value =
51094                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51095              
51096         }
51097     },
51098
51099     /**
51100      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51101      * query allowing the query action to be canceled if needed.
51102      * @param {String} query The SQL query to execute
51103      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51104      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51105      * saved in the current store (defaults to false)
51106      */
51107     doQuery : function(q, forceAll){
51108         
51109         Roo.log('doQuery?');
51110         if(q === undefined || q === null){
51111             q = '';
51112         }
51113         var qe = {
51114             query: q,
51115             forceAll: forceAll,
51116             combo: this,
51117             cancel:false
51118         };
51119         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51120             return false;
51121         }
51122         q = qe.query;
51123         forceAll = qe.forceAll;
51124         if(forceAll === true || (q.length >= this.minChars)){
51125             if(this.lastQuery != q || this.alwaysQuery){
51126                 this.lastQuery = q;
51127                 if(this.mode == 'local'){
51128                     this.selectedIndex = -1;
51129                     if(forceAll){
51130                         this.store.clearFilter();
51131                     }else{
51132                         this.store.filter(this.displayField, q);
51133                     }
51134                     this.onLoad();
51135                 }else{
51136                     this.store.baseParams[this.queryParam] = q;
51137                     this.store.load({
51138                         params: this.getParams(q)
51139                     });
51140                     this.expand();
51141                 }
51142             }else{
51143                 this.selectedIndex = -1;
51144                 this.onLoad();   
51145             }
51146         }
51147     },
51148
51149     // private
51150     getParams : function(q){
51151         var p = {};
51152         //p[this.queryParam] = q;
51153         if(this.pageSize){
51154             p.start = 0;
51155             p.limit = this.pageSize;
51156         }
51157         return p;
51158     },
51159
51160     /**
51161      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51162      */
51163     collapse : function(){
51164         
51165     },
51166
51167     // private
51168     collapseIf : function(e){
51169         
51170     },
51171
51172     /**
51173      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51174      */
51175     expand : function(){
51176         
51177     } ,
51178
51179     // private
51180      
51181
51182     /** 
51183     * @cfg {Boolean} grow 
51184     * @hide 
51185     */
51186     /** 
51187     * @cfg {Number} growMin 
51188     * @hide 
51189     */
51190     /** 
51191     * @cfg {Number} growMax 
51192     * @hide 
51193     */
51194     /**
51195      * @hide
51196      * @method autoSize
51197      */
51198     
51199     setWidth : function()
51200     {
51201         
51202     },
51203     getResizeEl : function(){
51204         return this.el;
51205     }
51206 });//<script type="text/javasscript">
51207  
51208
51209 /**
51210  * @class Roo.DDView
51211  * A DnD enabled version of Roo.View.
51212  * @param {Element/String} container The Element in which to create the View.
51213  * @param {String} tpl The template string used to create the markup for each element of the View
51214  * @param {Object} config The configuration properties. These include all the config options of
51215  * {@link Roo.View} plus some specific to this class.<br>
51216  * <p>
51217  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51218  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51219  * <p>
51220  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51221 .x-view-drag-insert-above {
51222         border-top:1px dotted #3366cc;
51223 }
51224 .x-view-drag-insert-below {
51225         border-bottom:1px dotted #3366cc;
51226 }
51227 </code></pre>
51228  * 
51229  */
51230  
51231 Roo.DDView = function(container, tpl, config) {
51232     Roo.DDView.superclass.constructor.apply(this, arguments);
51233     this.getEl().setStyle("outline", "0px none");
51234     this.getEl().unselectable();
51235     if (this.dragGroup) {
51236                 this.setDraggable(this.dragGroup.split(","));
51237     }
51238     if (this.dropGroup) {
51239                 this.setDroppable(this.dropGroup.split(","));
51240     }
51241     if (this.deletable) {
51242         this.setDeletable();
51243     }
51244     this.isDirtyFlag = false;
51245         this.addEvents({
51246                 "drop" : true
51247         });
51248 };
51249
51250 Roo.extend(Roo.DDView, Roo.View, {
51251 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51252 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51253 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51254 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51255
51256         isFormField: true,
51257
51258         reset: Roo.emptyFn,
51259         
51260         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51261
51262         validate: function() {
51263                 return true;
51264         },
51265         
51266         destroy: function() {
51267                 this.purgeListeners();
51268                 this.getEl.removeAllListeners();
51269                 this.getEl().remove();
51270                 if (this.dragZone) {
51271                         if (this.dragZone.destroy) {
51272                                 this.dragZone.destroy();
51273                         }
51274                 }
51275                 if (this.dropZone) {
51276                         if (this.dropZone.destroy) {
51277                                 this.dropZone.destroy();
51278                         }
51279                 }
51280         },
51281
51282 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51283         getName: function() {
51284                 return this.name;
51285         },
51286
51287 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51288         setValue: function(v) {
51289                 if (!this.store) {
51290                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51291                 }
51292                 var data = {};
51293                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51294                 this.store.proxy = new Roo.data.MemoryProxy(data);
51295                 this.store.load();
51296         },
51297
51298 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51299         getValue: function() {
51300                 var result = '(';
51301                 this.store.each(function(rec) {
51302                         result += rec.id + ',';
51303                 });
51304                 return result.substr(0, result.length - 1) + ')';
51305         },
51306         
51307         getIds: function() {
51308                 var i = 0, result = new Array(this.store.getCount());
51309                 this.store.each(function(rec) {
51310                         result[i++] = rec.id;
51311                 });
51312                 return result;
51313         },
51314         
51315         isDirty: function() {
51316                 return this.isDirtyFlag;
51317         },
51318
51319 /**
51320  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51321  *      whole Element becomes the target, and this causes the drop gesture to append.
51322  */
51323     getTargetFromEvent : function(e) {
51324                 var target = e.getTarget();
51325                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51326                 target = target.parentNode;
51327                 }
51328                 if (!target) {
51329                         target = this.el.dom.lastChild || this.el.dom;
51330                 }
51331                 return target;
51332     },
51333
51334 /**
51335  *      Create the drag data which consists of an object which has the property "ddel" as
51336  *      the drag proxy element. 
51337  */
51338     getDragData : function(e) {
51339         var target = this.findItemFromChild(e.getTarget());
51340                 if(target) {
51341                         this.handleSelection(e);
51342                         var selNodes = this.getSelectedNodes();
51343             var dragData = {
51344                 source: this,
51345                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51346                 nodes: selNodes,
51347                 records: []
51348                         };
51349                         var selectedIndices = this.getSelectedIndexes();
51350                         for (var i = 0; i < selectedIndices.length; i++) {
51351                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51352                         }
51353                         if (selNodes.length == 1) {
51354                                 dragData.ddel = target.cloneNode(true); // the div element
51355                         } else {
51356                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51357                                 div.className = 'multi-proxy';
51358                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51359                                         div.appendChild(selNodes[i].cloneNode(true));
51360                                 }
51361                                 dragData.ddel = div;
51362                         }
51363             //console.log(dragData)
51364             //console.log(dragData.ddel.innerHTML)
51365                         return dragData;
51366                 }
51367         //console.log('nodragData')
51368                 return false;
51369     },
51370     
51371 /**     Specify to which ddGroup items in this DDView may be dragged. */
51372     setDraggable: function(ddGroup) {
51373         if (ddGroup instanceof Array) {
51374                 Roo.each(ddGroup, this.setDraggable, this);
51375                 return;
51376         }
51377         if (this.dragZone) {
51378                 this.dragZone.addToGroup(ddGroup);
51379         } else {
51380                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51381                                 containerScroll: true,
51382                                 ddGroup: ddGroup 
51383
51384                         });
51385 //                      Draggability implies selection. DragZone's mousedown selects the element.
51386                         if (!this.multiSelect) { this.singleSelect = true; }
51387
51388 //                      Wire the DragZone's handlers up to methods in *this*
51389                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51390                 }
51391     },
51392
51393 /**     Specify from which ddGroup this DDView accepts drops. */
51394     setDroppable: function(ddGroup) {
51395         if (ddGroup instanceof Array) {
51396                 Roo.each(ddGroup, this.setDroppable, this);
51397                 return;
51398         }
51399         if (this.dropZone) {
51400                 this.dropZone.addToGroup(ddGroup);
51401         } else {
51402                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51403                                 containerScroll: true,
51404                                 ddGroup: ddGroup
51405                         });
51406
51407 //                      Wire the DropZone's handlers up to methods in *this*
51408                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51409                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51410                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51411                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51412                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51413                 }
51414     },
51415
51416 /**     Decide whether to drop above or below a View node. */
51417     getDropPoint : function(e, n, dd){
51418         if (n == this.el.dom) { return "above"; }
51419                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51420                 var c = t + (b - t) / 2;
51421                 var y = Roo.lib.Event.getPageY(e);
51422                 if(y <= c) {
51423                         return "above";
51424                 }else{
51425                         return "below";
51426                 }
51427     },
51428
51429     onNodeEnter : function(n, dd, e, data){
51430                 return false;
51431     },
51432     
51433     onNodeOver : function(n, dd, e, data){
51434                 var pt = this.getDropPoint(e, n, dd);
51435                 // set the insert point style on the target node
51436                 var dragElClass = this.dropNotAllowed;
51437                 if (pt) {
51438                         var targetElClass;
51439                         if (pt == "above"){
51440                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51441                                 targetElClass = "x-view-drag-insert-above";
51442                         } else {
51443                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51444                                 targetElClass = "x-view-drag-insert-below";
51445                         }
51446                         if (this.lastInsertClass != targetElClass){
51447                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51448                                 this.lastInsertClass = targetElClass;
51449                         }
51450                 }
51451                 return dragElClass;
51452         },
51453
51454     onNodeOut : function(n, dd, e, data){
51455                 this.removeDropIndicators(n);
51456     },
51457
51458     onNodeDrop : function(n, dd, e, data){
51459         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51460                 return false;
51461         }
51462         var pt = this.getDropPoint(e, n, dd);
51463                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51464                 if (pt == "below") { insertAt++; }
51465                 for (var i = 0; i < data.records.length; i++) {
51466                         var r = data.records[i];
51467                         var dup = this.store.getById(r.id);
51468                         if (dup && (dd != this.dragZone)) {
51469                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51470                         } else {
51471                                 if (data.copy) {
51472                                         this.store.insert(insertAt++, r.copy());
51473                                 } else {
51474                                         data.source.isDirtyFlag = true;
51475                                         r.store.remove(r);
51476                                         this.store.insert(insertAt++, r);
51477                                 }
51478                                 this.isDirtyFlag = true;
51479                         }
51480                 }
51481                 this.dragZone.cachedTarget = null;
51482                 return true;
51483     },
51484
51485     removeDropIndicators : function(n){
51486                 if(n){
51487                         Roo.fly(n).removeClass([
51488                                 "x-view-drag-insert-above",
51489                                 "x-view-drag-insert-below"]);
51490                         this.lastInsertClass = "_noclass";
51491                 }
51492     },
51493
51494 /**
51495  *      Utility method. Add a delete option to the DDView's context menu.
51496  *      @param {String} imageUrl The URL of the "delete" icon image.
51497  */
51498         setDeletable: function(imageUrl) {
51499                 if (!this.singleSelect && !this.multiSelect) {
51500                         this.singleSelect = true;
51501                 }
51502                 var c = this.getContextMenu();
51503                 this.contextMenu.on("itemclick", function(item) {
51504                         switch (item.id) {
51505                                 case "delete":
51506                                         this.remove(this.getSelectedIndexes());
51507                                         break;
51508                         }
51509                 }, this);
51510                 this.contextMenu.add({
51511                         icon: imageUrl,
51512                         id: "delete",
51513                         text: 'Delete'
51514                 });
51515         },
51516         
51517 /**     Return the context menu for this DDView. */
51518         getContextMenu: function() {
51519                 if (!this.contextMenu) {
51520 //                      Create the View's context menu
51521                         this.contextMenu = new Roo.menu.Menu({
51522                                 id: this.id + "-contextmenu"
51523                         });
51524                         this.el.on("contextmenu", this.showContextMenu, this);
51525                 }
51526                 return this.contextMenu;
51527         },
51528         
51529         disableContextMenu: function() {
51530                 if (this.contextMenu) {
51531                         this.el.un("contextmenu", this.showContextMenu, this);
51532                 }
51533         },
51534
51535         showContextMenu: function(e, item) {
51536         item = this.findItemFromChild(e.getTarget());
51537                 if (item) {
51538                         e.stopEvent();
51539                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51540                         this.contextMenu.showAt(e.getXY());
51541             }
51542     },
51543
51544 /**
51545  *      Remove {@link Roo.data.Record}s at the specified indices.
51546  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51547  */
51548     remove: function(selectedIndices) {
51549                 selectedIndices = [].concat(selectedIndices);
51550                 for (var i = 0; i < selectedIndices.length; i++) {
51551                         var rec = this.store.getAt(selectedIndices[i]);
51552                         this.store.remove(rec);
51553                 }
51554     },
51555
51556 /**
51557  *      Double click fires the event, but also, if this is draggable, and there is only one other
51558  *      related DropZone, it transfers the selected node.
51559  */
51560     onDblClick : function(e){
51561         var item = this.findItemFromChild(e.getTarget());
51562         if(item){
51563             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51564                 return false;
51565             }
51566             if (this.dragGroup) {
51567                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51568                     while (targets.indexOf(this.dropZone) > -1) {
51569                             targets.remove(this.dropZone);
51570                                 }
51571                     if (targets.length == 1) {
51572                                         this.dragZone.cachedTarget = null;
51573                         var el = Roo.get(targets[0].getEl());
51574                         var box = el.getBox(true);
51575                         targets[0].onNodeDrop(el.dom, {
51576                                 target: el.dom,
51577                                 xy: [box.x, box.y + box.height - 1]
51578                         }, null, this.getDragData(e));
51579                     }
51580                 }
51581         }
51582     },
51583     
51584     handleSelection: function(e) {
51585                 this.dragZone.cachedTarget = null;
51586         var item = this.findItemFromChild(e.getTarget());
51587         if (!item) {
51588                 this.clearSelections(true);
51589                 return;
51590         }
51591                 if (item && (this.multiSelect || this.singleSelect)){
51592                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51593                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51594                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51595                                 this.unselect(item);
51596                         } else {
51597                                 this.select(item, this.multiSelect && e.ctrlKey);
51598                                 this.lastSelection = item;
51599                         }
51600                 }
51601     },
51602
51603     onItemClick : function(item, index, e){
51604                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51605                         return false;
51606                 }
51607                 return true;
51608     },
51609
51610     unselect : function(nodeInfo, suppressEvent){
51611                 var node = this.getNode(nodeInfo);
51612                 if(node && this.isSelected(node)){
51613                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51614                                 Roo.fly(node).removeClass(this.selectedClass);
51615                                 this.selections.remove(node);
51616                                 if(!suppressEvent){
51617                                         this.fireEvent("selectionchange", this, this.selections);
51618                                 }
51619                         }
51620                 }
51621     }
51622 });
51623 /*
51624  * Based on:
51625  * Ext JS Library 1.1.1
51626  * Copyright(c) 2006-2007, Ext JS, LLC.
51627  *
51628  * Originally Released Under LGPL - original licence link has changed is not relivant.
51629  *
51630  * Fork - LGPL
51631  * <script type="text/javascript">
51632  */
51633  
51634 /**
51635  * @class Roo.LayoutManager
51636  * @extends Roo.util.Observable
51637  * Base class for layout managers.
51638  */
51639 Roo.LayoutManager = function(container, config){
51640     Roo.LayoutManager.superclass.constructor.call(this);
51641     this.el = Roo.get(container);
51642     // ie scrollbar fix
51643     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
51644         document.body.scroll = "no";
51645     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
51646         this.el.position('relative');
51647     }
51648     this.id = this.el.id;
51649     this.el.addClass("x-layout-container");
51650     /** false to disable window resize monitoring @type Boolean */
51651     this.monitorWindowResize = true;
51652     this.regions = {};
51653     this.addEvents({
51654         /**
51655          * @event layout
51656          * Fires when a layout is performed. 
51657          * @param {Roo.LayoutManager} this
51658          */
51659         "layout" : true,
51660         /**
51661          * @event regionresized
51662          * Fires when the user resizes a region. 
51663          * @param {Roo.LayoutRegion} region The resized region
51664          * @param {Number} newSize The new size (width for east/west, height for north/south)
51665          */
51666         "regionresized" : true,
51667         /**
51668          * @event regioncollapsed
51669          * Fires when a region is collapsed. 
51670          * @param {Roo.LayoutRegion} region The collapsed region
51671          */
51672         "regioncollapsed" : true,
51673         /**
51674          * @event regionexpanded
51675          * Fires when a region is expanded.  
51676          * @param {Roo.LayoutRegion} region The expanded region
51677          */
51678         "regionexpanded" : true
51679     });
51680     this.updating = false;
51681     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51682 };
51683
51684 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
51685     /**
51686      * Returns true if this layout is currently being updated
51687      * @return {Boolean}
51688      */
51689     isUpdating : function(){
51690         return this.updating; 
51691     },
51692     
51693     /**
51694      * Suspend the LayoutManager from doing auto-layouts while
51695      * making multiple add or remove calls
51696      */
51697     beginUpdate : function(){
51698         this.updating = true;    
51699     },
51700     
51701     /**
51702      * Restore auto-layouts and optionally disable the manager from performing a layout
51703      * @param {Boolean} noLayout true to disable a layout update 
51704      */
51705     endUpdate : function(noLayout){
51706         this.updating = false;
51707         if(!noLayout){
51708             this.layout();
51709         }    
51710     },
51711     
51712     layout: function(){
51713         
51714     },
51715     
51716     onRegionResized : function(region, newSize){
51717         this.fireEvent("regionresized", region, newSize);
51718         this.layout();
51719     },
51720     
51721     onRegionCollapsed : function(region){
51722         this.fireEvent("regioncollapsed", region);
51723     },
51724     
51725     onRegionExpanded : function(region){
51726         this.fireEvent("regionexpanded", region);
51727     },
51728         
51729     /**
51730      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
51731      * performs box-model adjustments.
51732      * @return {Object} The size as an object {width: (the width), height: (the height)}
51733      */
51734     getViewSize : function(){
51735         var size;
51736         if(this.el.dom != document.body){
51737             size = this.el.getSize();
51738         }else{
51739             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
51740         }
51741         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
51742         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
51743         return size;
51744     },
51745     
51746     /**
51747      * Returns the Element this layout is bound to.
51748      * @return {Roo.Element}
51749      */
51750     getEl : function(){
51751         return this.el;
51752     },
51753     
51754     /**
51755      * Returns the specified region.
51756      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
51757      * @return {Roo.LayoutRegion}
51758      */
51759     getRegion : function(target){
51760         return this.regions[target.toLowerCase()];
51761     },
51762     
51763     onWindowResize : function(){
51764         if(this.monitorWindowResize){
51765             this.layout();
51766         }
51767     }
51768 });/*
51769  * Based on:
51770  * Ext JS Library 1.1.1
51771  * Copyright(c) 2006-2007, Ext JS, LLC.
51772  *
51773  * Originally Released Under LGPL - original licence link has changed is not relivant.
51774  *
51775  * Fork - LGPL
51776  * <script type="text/javascript">
51777  */
51778 /**
51779  * @class Roo.BorderLayout
51780  * @extends Roo.LayoutManager
51781  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
51782  * please see: <br><br>
51783  * <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>
51784  * <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>
51785  * Example:
51786  <pre><code>
51787  var layout = new Roo.BorderLayout(document.body, {
51788     north: {
51789         initialSize: 25,
51790         titlebar: false
51791     },
51792     west: {
51793         split:true,
51794         initialSize: 200,
51795         minSize: 175,
51796         maxSize: 400,
51797         titlebar: true,
51798         collapsible: true
51799     },
51800     east: {
51801         split:true,
51802         initialSize: 202,
51803         minSize: 175,
51804         maxSize: 400,
51805         titlebar: true,
51806         collapsible: true
51807     },
51808     south: {
51809         split:true,
51810         initialSize: 100,
51811         minSize: 100,
51812         maxSize: 200,
51813         titlebar: true,
51814         collapsible: true
51815     },
51816     center: {
51817         titlebar: true,
51818         autoScroll:true,
51819         resizeTabs: true,
51820         minTabWidth: 50,
51821         preferredTabWidth: 150
51822     }
51823 });
51824
51825 // shorthand
51826 var CP = Roo.ContentPanel;
51827
51828 layout.beginUpdate();
51829 layout.add("north", new CP("north", "North"));
51830 layout.add("south", new CP("south", {title: "South", closable: true}));
51831 layout.add("west", new CP("west", {title: "West"}));
51832 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
51833 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
51834 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
51835 layout.getRegion("center").showPanel("center1");
51836 layout.endUpdate();
51837 </code></pre>
51838
51839 <b>The container the layout is rendered into can be either the body element or any other element.
51840 If it is not the body element, the container needs to either be an absolute positioned element,
51841 or you will need to add "position:relative" to the css of the container.  You will also need to specify
51842 the container size if it is not the body element.</b>
51843
51844 * @constructor
51845 * Create a new BorderLayout
51846 * @param {String/HTMLElement/Element} container The container this layout is bound to
51847 * @param {Object} config Configuration options
51848  */
51849 Roo.BorderLayout = function(container, config){
51850     config = config || {};
51851     Roo.BorderLayout.superclass.constructor.call(this, container, config);
51852     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
51853     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
51854         var target = this.factory.validRegions[i];
51855         if(config[target]){
51856             this.addRegion(target, config[target]);
51857         }
51858     }
51859 };
51860
51861 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
51862     /**
51863      * Creates and adds a new region if it doesn't already exist.
51864      * @param {String} target The target region key (north, south, east, west or center).
51865      * @param {Object} config The regions config object
51866      * @return {BorderLayoutRegion} The new region
51867      */
51868     addRegion : function(target, config){
51869         if(!this.regions[target]){
51870             var r = this.factory.create(target, this, config);
51871             this.bindRegion(target, r);
51872         }
51873         return this.regions[target];
51874     },
51875
51876     // private (kinda)
51877     bindRegion : function(name, r){
51878         this.regions[name] = r;
51879         r.on("visibilitychange", this.layout, this);
51880         r.on("paneladded", this.layout, this);
51881         r.on("panelremoved", this.layout, this);
51882         r.on("invalidated", this.layout, this);
51883         r.on("resized", this.onRegionResized, this);
51884         r.on("collapsed", this.onRegionCollapsed, this);
51885         r.on("expanded", this.onRegionExpanded, this);
51886     },
51887
51888     /**
51889      * Performs a layout update.
51890      */
51891     layout : function(){
51892         if(this.updating) {
51893             return;
51894         }
51895         var size = this.getViewSize();
51896         var w = size.width;
51897         var h = size.height;
51898         var centerW = w;
51899         var centerH = h;
51900         var centerY = 0;
51901         var centerX = 0;
51902         //var x = 0, y = 0;
51903
51904         var rs = this.regions;
51905         var north = rs["north"];
51906         var south = rs["south"]; 
51907         var west = rs["west"];
51908         var east = rs["east"];
51909         var center = rs["center"];
51910         //if(this.hideOnLayout){ // not supported anymore
51911             //c.el.setStyle("display", "none");
51912         //}
51913         if(north && north.isVisible()){
51914             var b = north.getBox();
51915             var m = north.getMargins();
51916             b.width = w - (m.left+m.right);
51917             b.x = m.left;
51918             b.y = m.top;
51919             centerY = b.height + b.y + m.bottom;
51920             centerH -= centerY;
51921             north.updateBox(this.safeBox(b));
51922         }
51923         if(south && south.isVisible()){
51924             var b = south.getBox();
51925             var m = south.getMargins();
51926             b.width = w - (m.left+m.right);
51927             b.x = m.left;
51928             var totalHeight = (b.height + m.top + m.bottom);
51929             b.y = h - totalHeight + m.top;
51930             centerH -= totalHeight;
51931             south.updateBox(this.safeBox(b));
51932         }
51933         if(west && west.isVisible()){
51934             var b = west.getBox();
51935             var m = west.getMargins();
51936             b.height = centerH - (m.top+m.bottom);
51937             b.x = m.left;
51938             b.y = centerY + m.top;
51939             var totalWidth = (b.width + m.left + m.right);
51940             centerX += totalWidth;
51941             centerW -= totalWidth;
51942             west.updateBox(this.safeBox(b));
51943         }
51944         if(east && east.isVisible()){
51945             var b = east.getBox();
51946             var m = east.getMargins();
51947             b.height = centerH - (m.top+m.bottom);
51948             var totalWidth = (b.width + m.left + m.right);
51949             b.x = w - totalWidth + m.left;
51950             b.y = centerY + m.top;
51951             centerW -= totalWidth;
51952             east.updateBox(this.safeBox(b));
51953         }
51954         if(center){
51955             var m = center.getMargins();
51956             var centerBox = {
51957                 x: centerX + m.left,
51958                 y: centerY + m.top,
51959                 width: centerW - (m.left+m.right),
51960                 height: centerH - (m.top+m.bottom)
51961             };
51962             //if(this.hideOnLayout){
51963                 //center.el.setStyle("display", "block");
51964             //}
51965             center.updateBox(this.safeBox(centerBox));
51966         }
51967         this.el.repaint();
51968         this.fireEvent("layout", this);
51969     },
51970
51971     // private
51972     safeBox : function(box){
51973         box.width = Math.max(0, box.width);
51974         box.height = Math.max(0, box.height);
51975         return box;
51976     },
51977
51978     /**
51979      * Adds a ContentPanel (or subclass) to this layout.
51980      * @param {String} target The target region key (north, south, east, west or center).
51981      * @param {Roo.ContentPanel} panel The panel to add
51982      * @return {Roo.ContentPanel} The added panel
51983      */
51984     add : function(target, panel){
51985          
51986         target = target.toLowerCase();
51987         return this.regions[target].add(panel);
51988     },
51989
51990     /**
51991      * Remove a ContentPanel (or subclass) to this layout.
51992      * @param {String} target The target region key (north, south, east, west or center).
51993      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51994      * @return {Roo.ContentPanel} The removed panel
51995      */
51996     remove : function(target, panel){
51997         target = target.toLowerCase();
51998         return this.regions[target].remove(panel);
51999     },
52000
52001     /**
52002      * Searches all regions for a panel with the specified id
52003      * @param {String} panelId
52004      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52005      */
52006     findPanel : function(panelId){
52007         var rs = this.regions;
52008         for(var target in rs){
52009             if(typeof rs[target] != "function"){
52010                 var p = rs[target].getPanel(panelId);
52011                 if(p){
52012                     return p;
52013                 }
52014             }
52015         }
52016         return null;
52017     },
52018
52019     /**
52020      * Searches all regions for a panel with the specified id and activates (shows) it.
52021      * @param {String/ContentPanel} panelId The panels id or the panel itself
52022      * @return {Roo.ContentPanel} The shown panel or null
52023      */
52024     showPanel : function(panelId) {
52025       var rs = this.regions;
52026       for(var target in rs){
52027          var r = rs[target];
52028          if(typeof r != "function"){
52029             if(r.hasPanel(panelId)){
52030                return r.showPanel(panelId);
52031             }
52032          }
52033       }
52034       return null;
52035    },
52036
52037    /**
52038      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52039      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52040      */
52041     restoreState : function(provider){
52042         if(!provider){
52043             provider = Roo.state.Manager;
52044         }
52045         var sm = new Roo.LayoutStateManager();
52046         sm.init(this, provider);
52047     },
52048
52049     /**
52050      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52051      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52052      * a valid ContentPanel config object.  Example:
52053      * <pre><code>
52054 // Create the main layout
52055 var layout = new Roo.BorderLayout('main-ct', {
52056     west: {
52057         split:true,
52058         minSize: 175,
52059         titlebar: true
52060     },
52061     center: {
52062         title:'Components'
52063     }
52064 }, 'main-ct');
52065
52066 // Create and add multiple ContentPanels at once via configs
52067 layout.batchAdd({
52068    west: {
52069        id: 'source-files',
52070        autoCreate:true,
52071        title:'Ext Source Files',
52072        autoScroll:true,
52073        fitToFrame:true
52074    },
52075    center : {
52076        el: cview,
52077        autoScroll:true,
52078        fitToFrame:true,
52079        toolbar: tb,
52080        resizeEl:'cbody'
52081    }
52082 });
52083 </code></pre>
52084      * @param {Object} regions An object containing ContentPanel configs by region name
52085      */
52086     batchAdd : function(regions){
52087         this.beginUpdate();
52088         for(var rname in regions){
52089             var lr = this.regions[rname];
52090             if(lr){
52091                 this.addTypedPanels(lr, regions[rname]);
52092             }
52093         }
52094         this.endUpdate();
52095     },
52096
52097     // private
52098     addTypedPanels : function(lr, ps){
52099         if(typeof ps == 'string'){
52100             lr.add(new Roo.ContentPanel(ps));
52101         }
52102         else if(ps instanceof Array){
52103             for(var i =0, len = ps.length; i < len; i++){
52104                 this.addTypedPanels(lr, ps[i]);
52105             }
52106         }
52107         else if(!ps.events){ // raw config?
52108             var el = ps.el;
52109             delete ps.el; // prevent conflict
52110             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52111         }
52112         else {  // panel object assumed!
52113             lr.add(ps);
52114         }
52115     },
52116     /**
52117      * Adds a xtype elements to the layout.
52118      * <pre><code>
52119
52120 layout.addxtype({
52121        xtype : 'ContentPanel',
52122        region: 'west',
52123        items: [ .... ]
52124    }
52125 );
52126
52127 layout.addxtype({
52128         xtype : 'NestedLayoutPanel',
52129         region: 'west',
52130         layout: {
52131            center: { },
52132            west: { }   
52133         },
52134         items : [ ... list of content panels or nested layout panels.. ]
52135    }
52136 );
52137 </code></pre>
52138      * @param {Object} cfg Xtype definition of item to add.
52139      */
52140     addxtype : function(cfg)
52141     {
52142         // basically accepts a pannel...
52143         // can accept a layout region..!?!?
52144         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52145         
52146         if (!cfg.xtype.match(/Panel$/)) {
52147             return false;
52148         }
52149         var ret = false;
52150         
52151         if (typeof(cfg.region) == 'undefined') {
52152             Roo.log("Failed to add Panel, region was not set");
52153             Roo.log(cfg);
52154             return false;
52155         }
52156         var region = cfg.region;
52157         delete cfg.region;
52158         
52159           
52160         var xitems = [];
52161         if (cfg.items) {
52162             xitems = cfg.items;
52163             delete cfg.items;
52164         }
52165         var nb = false;
52166         
52167         switch(cfg.xtype) 
52168         {
52169             case 'ContentPanel':  // ContentPanel (el, cfg)
52170             case 'ScrollPanel':  // ContentPanel (el, cfg)
52171             case 'ViewPanel': 
52172                 if(cfg.autoCreate) {
52173                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52174                 } else {
52175                     var el = this.el.createChild();
52176                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52177                 }
52178                 
52179                 this.add(region, ret);
52180                 break;
52181             
52182             
52183             case 'TreePanel': // our new panel!
52184                 cfg.el = this.el.createChild();
52185                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52186                 this.add(region, ret);
52187                 break;
52188             
52189             case 'NestedLayoutPanel': 
52190                 // create a new Layout (which is  a Border Layout...
52191                 var el = this.el.createChild();
52192                 var clayout = cfg.layout;
52193                 delete cfg.layout;
52194                 clayout.items   = clayout.items  || [];
52195                 // replace this exitems with the clayout ones..
52196                 xitems = clayout.items;
52197                  
52198                 
52199                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52200                     cfg.background = false;
52201                 }
52202                 var layout = new Roo.BorderLayout(el, clayout);
52203                 
52204                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52205                 //console.log('adding nested layout panel '  + cfg.toSource());
52206                 this.add(region, ret);
52207                 nb = {}; /// find first...
52208                 break;
52209                 
52210             case 'GridPanel': 
52211             
52212                 // needs grid and region
52213                 
52214                 //var el = this.getRegion(region).el.createChild();
52215                 var el = this.el.createChild();
52216                 // create the grid first...
52217                 
52218                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52219                 delete cfg.grid;
52220                 if (region == 'center' && this.active ) {
52221                     cfg.background = false;
52222                 }
52223                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52224                 
52225                 this.add(region, ret);
52226                 if (cfg.background) {
52227                     ret.on('activate', function(gp) {
52228                         if (!gp.grid.rendered) {
52229                             gp.grid.render();
52230                         }
52231                     });
52232                 } else {
52233                     grid.render();
52234                 }
52235                 break;
52236            
52237            
52238            
52239                 
52240                 
52241                 
52242             default:
52243                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52244                     
52245                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52246                     this.add(region, ret);
52247                 } else {
52248                 
52249                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52250                     return null;
52251                 }
52252                 
52253              // GridPanel (grid, cfg)
52254             
52255         }
52256         this.beginUpdate();
52257         // add children..
52258         var region = '';
52259         var abn = {};
52260         Roo.each(xitems, function(i)  {
52261             region = nb && i.region ? i.region : false;
52262             
52263             var add = ret.addxtype(i);
52264            
52265             if (region) {
52266                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52267                 if (!i.background) {
52268                     abn[region] = nb[region] ;
52269                 }
52270             }
52271             
52272         });
52273         this.endUpdate();
52274
52275         // make the last non-background panel active..
52276         //if (nb) { Roo.log(abn); }
52277         if (nb) {
52278             
52279             for(var r in abn) {
52280                 region = this.getRegion(r);
52281                 if (region) {
52282                     // tried using nb[r], but it does not work..
52283                      
52284                     region.showPanel(abn[r]);
52285                    
52286                 }
52287             }
52288         }
52289         return ret;
52290         
52291     }
52292 });
52293
52294 /**
52295  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52296  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52297  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52298  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52299  * <pre><code>
52300 // shorthand
52301 var CP = Roo.ContentPanel;
52302
52303 var layout = Roo.BorderLayout.create({
52304     north: {
52305         initialSize: 25,
52306         titlebar: false,
52307         panels: [new CP("north", "North")]
52308     },
52309     west: {
52310         split:true,
52311         initialSize: 200,
52312         minSize: 175,
52313         maxSize: 400,
52314         titlebar: true,
52315         collapsible: true,
52316         panels: [new CP("west", {title: "West"})]
52317     },
52318     east: {
52319         split:true,
52320         initialSize: 202,
52321         minSize: 175,
52322         maxSize: 400,
52323         titlebar: true,
52324         collapsible: true,
52325         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52326     },
52327     south: {
52328         split:true,
52329         initialSize: 100,
52330         minSize: 100,
52331         maxSize: 200,
52332         titlebar: true,
52333         collapsible: true,
52334         panels: [new CP("south", {title: "South", closable: true})]
52335     },
52336     center: {
52337         titlebar: true,
52338         autoScroll:true,
52339         resizeTabs: true,
52340         minTabWidth: 50,
52341         preferredTabWidth: 150,
52342         panels: [
52343             new CP("center1", {title: "Close Me", closable: true}),
52344             new CP("center2", {title: "Center Panel", closable: false})
52345         ]
52346     }
52347 }, document.body);
52348
52349 layout.getRegion("center").showPanel("center1");
52350 </code></pre>
52351  * @param config
52352  * @param targetEl
52353  */
52354 Roo.BorderLayout.create = function(config, targetEl){
52355     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52356     layout.beginUpdate();
52357     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52358     for(var j = 0, jlen = regions.length; j < jlen; j++){
52359         var lr = regions[j];
52360         if(layout.regions[lr] && config[lr].panels){
52361             var r = layout.regions[lr];
52362             var ps = config[lr].panels;
52363             layout.addTypedPanels(r, ps);
52364         }
52365     }
52366     layout.endUpdate();
52367     return layout;
52368 };
52369
52370 // private
52371 Roo.BorderLayout.RegionFactory = {
52372     // private
52373     validRegions : ["north","south","east","west","center"],
52374
52375     // private
52376     create : function(target, mgr, config){
52377         target = target.toLowerCase();
52378         if(config.lightweight || config.basic){
52379             return new Roo.BasicLayoutRegion(mgr, config, target);
52380         }
52381         switch(target){
52382             case "north":
52383                 return new Roo.NorthLayoutRegion(mgr, config);
52384             case "south":
52385                 return new Roo.SouthLayoutRegion(mgr, config);
52386             case "east":
52387                 return new Roo.EastLayoutRegion(mgr, config);
52388             case "west":
52389                 return new Roo.WestLayoutRegion(mgr, config);
52390             case "center":
52391                 return new Roo.CenterLayoutRegion(mgr, config);
52392         }
52393         throw 'Layout region "'+target+'" not supported.';
52394     }
52395 };/*
52396  * Based on:
52397  * Ext JS Library 1.1.1
52398  * Copyright(c) 2006-2007, Ext JS, LLC.
52399  *
52400  * Originally Released Under LGPL - original licence link has changed is not relivant.
52401  *
52402  * Fork - LGPL
52403  * <script type="text/javascript">
52404  */
52405  
52406 /**
52407  * @class Roo.BasicLayoutRegion
52408  * @extends Roo.util.Observable
52409  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52410  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52411  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52412  */
52413 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52414     this.mgr = mgr;
52415     this.position  = pos;
52416     this.events = {
52417         /**
52418          * @scope Roo.BasicLayoutRegion
52419          */
52420         
52421         /**
52422          * @event beforeremove
52423          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52424          * @param {Roo.LayoutRegion} this
52425          * @param {Roo.ContentPanel} panel The panel
52426          * @param {Object} e The cancel event object
52427          */
52428         "beforeremove" : true,
52429         /**
52430          * @event invalidated
52431          * Fires when the layout for this region is changed.
52432          * @param {Roo.LayoutRegion} this
52433          */
52434         "invalidated" : true,
52435         /**
52436          * @event visibilitychange
52437          * Fires when this region is shown or hidden 
52438          * @param {Roo.LayoutRegion} this
52439          * @param {Boolean} visibility true or false
52440          */
52441         "visibilitychange" : true,
52442         /**
52443          * @event paneladded
52444          * Fires when a panel is added. 
52445          * @param {Roo.LayoutRegion} this
52446          * @param {Roo.ContentPanel} panel The panel
52447          */
52448         "paneladded" : true,
52449         /**
52450          * @event panelremoved
52451          * Fires when a panel is removed. 
52452          * @param {Roo.LayoutRegion} this
52453          * @param {Roo.ContentPanel} panel The panel
52454          */
52455         "panelremoved" : true,
52456         /**
52457          * @event beforecollapse
52458          * Fires when this region before collapse.
52459          * @param {Roo.LayoutRegion} this
52460          */
52461         "beforecollapse" : true,
52462         /**
52463          * @event collapsed
52464          * Fires when this region is collapsed.
52465          * @param {Roo.LayoutRegion} this
52466          */
52467         "collapsed" : true,
52468         /**
52469          * @event expanded
52470          * Fires when this region is expanded.
52471          * @param {Roo.LayoutRegion} this
52472          */
52473         "expanded" : true,
52474         /**
52475          * @event slideshow
52476          * Fires when this region is slid into view.
52477          * @param {Roo.LayoutRegion} this
52478          */
52479         "slideshow" : true,
52480         /**
52481          * @event slidehide
52482          * Fires when this region slides out of view. 
52483          * @param {Roo.LayoutRegion} this
52484          */
52485         "slidehide" : true,
52486         /**
52487          * @event panelactivated
52488          * Fires when a panel is activated. 
52489          * @param {Roo.LayoutRegion} this
52490          * @param {Roo.ContentPanel} panel The activated panel
52491          */
52492         "panelactivated" : true,
52493         /**
52494          * @event resized
52495          * Fires when the user resizes this region. 
52496          * @param {Roo.LayoutRegion} this
52497          * @param {Number} newSize The new size (width for east/west, height for north/south)
52498          */
52499         "resized" : true
52500     };
52501     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52502     this.panels = new Roo.util.MixedCollection();
52503     this.panels.getKey = this.getPanelId.createDelegate(this);
52504     this.box = null;
52505     this.activePanel = null;
52506     // ensure listeners are added...
52507     
52508     if (config.listeners || config.events) {
52509         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52510             listeners : config.listeners || {},
52511             events : config.events || {}
52512         });
52513     }
52514     
52515     if(skipConfig !== true){
52516         this.applyConfig(config);
52517     }
52518 };
52519
52520 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52521     getPanelId : function(p){
52522         return p.getId();
52523     },
52524     
52525     applyConfig : function(config){
52526         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52527         this.config = config;
52528         
52529     },
52530     
52531     /**
52532      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52533      * the width, for horizontal (north, south) the height.
52534      * @param {Number} newSize The new width or height
52535      */
52536     resizeTo : function(newSize){
52537         var el = this.el ? this.el :
52538                  (this.activePanel ? this.activePanel.getEl() : null);
52539         if(el){
52540             switch(this.position){
52541                 case "east":
52542                 case "west":
52543                     el.setWidth(newSize);
52544                     this.fireEvent("resized", this, newSize);
52545                 break;
52546                 case "north":
52547                 case "south":
52548                     el.setHeight(newSize);
52549                     this.fireEvent("resized", this, newSize);
52550                 break;                
52551             }
52552         }
52553     },
52554     
52555     getBox : function(){
52556         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52557     },
52558     
52559     getMargins : function(){
52560         return this.margins;
52561     },
52562     
52563     updateBox : function(box){
52564         this.box = box;
52565         var el = this.activePanel.getEl();
52566         el.dom.style.left = box.x + "px";
52567         el.dom.style.top = box.y + "px";
52568         this.activePanel.setSize(box.width, box.height);
52569     },
52570     
52571     /**
52572      * Returns the container element for this region.
52573      * @return {Roo.Element}
52574      */
52575     getEl : function(){
52576         return this.activePanel;
52577     },
52578     
52579     /**
52580      * Returns true if this region is currently visible.
52581      * @return {Boolean}
52582      */
52583     isVisible : function(){
52584         return this.activePanel ? true : false;
52585     },
52586     
52587     setActivePanel : function(panel){
52588         panel = this.getPanel(panel);
52589         if(this.activePanel && this.activePanel != panel){
52590             this.activePanel.setActiveState(false);
52591             this.activePanel.getEl().setLeftTop(-10000,-10000);
52592         }
52593         this.activePanel = panel;
52594         panel.setActiveState(true);
52595         if(this.box){
52596             panel.setSize(this.box.width, this.box.height);
52597         }
52598         this.fireEvent("panelactivated", this, panel);
52599         this.fireEvent("invalidated");
52600     },
52601     
52602     /**
52603      * Show the specified panel.
52604      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52605      * @return {Roo.ContentPanel} The shown panel or null
52606      */
52607     showPanel : function(panel){
52608         if(panel = this.getPanel(panel)){
52609             this.setActivePanel(panel);
52610         }
52611         return panel;
52612     },
52613     
52614     /**
52615      * Get the active panel for this region.
52616      * @return {Roo.ContentPanel} The active panel or null
52617      */
52618     getActivePanel : function(){
52619         return this.activePanel;
52620     },
52621     
52622     /**
52623      * Add the passed ContentPanel(s)
52624      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52625      * @return {Roo.ContentPanel} The panel added (if only one was added)
52626      */
52627     add : function(panel){
52628         if(arguments.length > 1){
52629             for(var i = 0, len = arguments.length; i < len; i++) {
52630                 this.add(arguments[i]);
52631             }
52632             return null;
52633         }
52634         if(this.hasPanel(panel)){
52635             this.showPanel(panel);
52636             return panel;
52637         }
52638         var el = panel.getEl();
52639         if(el.dom.parentNode != this.mgr.el.dom){
52640             this.mgr.el.dom.appendChild(el.dom);
52641         }
52642         if(panel.setRegion){
52643             panel.setRegion(this);
52644         }
52645         this.panels.add(panel);
52646         el.setStyle("position", "absolute");
52647         if(!panel.background){
52648             this.setActivePanel(panel);
52649             if(this.config.initialSize && this.panels.getCount()==1){
52650                 this.resizeTo(this.config.initialSize);
52651             }
52652         }
52653         this.fireEvent("paneladded", this, panel);
52654         return panel;
52655     },
52656     
52657     /**
52658      * Returns true if the panel is in this region.
52659      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52660      * @return {Boolean}
52661      */
52662     hasPanel : function(panel){
52663         if(typeof panel == "object"){ // must be panel obj
52664             panel = panel.getId();
52665         }
52666         return this.getPanel(panel) ? true : false;
52667     },
52668     
52669     /**
52670      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52671      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52672      * @param {Boolean} preservePanel Overrides the config preservePanel option
52673      * @return {Roo.ContentPanel} The panel that was removed
52674      */
52675     remove : function(panel, preservePanel){
52676         panel = this.getPanel(panel);
52677         if(!panel){
52678             return null;
52679         }
52680         var e = {};
52681         this.fireEvent("beforeremove", this, panel, e);
52682         if(e.cancel === true){
52683             return null;
52684         }
52685         var panelId = panel.getId();
52686         this.panels.removeKey(panelId);
52687         return panel;
52688     },
52689     
52690     /**
52691      * Returns the panel specified or null if it's not in this region.
52692      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52693      * @return {Roo.ContentPanel}
52694      */
52695     getPanel : function(id){
52696         if(typeof id == "object"){ // must be panel obj
52697             return id;
52698         }
52699         return this.panels.get(id);
52700     },
52701     
52702     /**
52703      * Returns this regions position (north/south/east/west/center).
52704      * @return {String} 
52705      */
52706     getPosition: function(){
52707         return this.position;    
52708     }
52709 });/*
52710  * Based on:
52711  * Ext JS Library 1.1.1
52712  * Copyright(c) 2006-2007, Ext JS, LLC.
52713  *
52714  * Originally Released Under LGPL - original licence link has changed is not relivant.
52715  *
52716  * Fork - LGPL
52717  * <script type="text/javascript">
52718  */
52719  
52720 /**
52721  * @class Roo.LayoutRegion
52722  * @extends Roo.BasicLayoutRegion
52723  * This class represents a region in a layout manager.
52724  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
52725  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
52726  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
52727  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
52728  * @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})
52729  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
52730  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
52731  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
52732  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
52733  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
52734  * @cfg {String}    title           The title for the region (overrides panel titles)
52735  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
52736  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
52737  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
52738  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
52739  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
52740  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
52741  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
52742  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
52743  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
52744  * @cfg {Boolean}   showPin         True to show a pin button
52745  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
52746  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
52747  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
52748  * @cfg {Number}    width           For East/West panels
52749  * @cfg {Number}    height          For North/South panels
52750  * @cfg {Boolean}   split           To show the splitter
52751  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
52752  */
52753 Roo.LayoutRegion = function(mgr, config, pos){
52754     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
52755     var dh = Roo.DomHelper;
52756     /** This region's container element 
52757     * @type Roo.Element */
52758     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
52759     /** This region's title element 
52760     * @type Roo.Element */
52761
52762     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
52763         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
52764         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
52765     ]}, true);
52766     this.titleEl.enableDisplayMode();
52767     /** This region's title text element 
52768     * @type HTMLElement */
52769     this.titleTextEl = this.titleEl.dom.firstChild;
52770     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
52771     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
52772     this.closeBtn.enableDisplayMode();
52773     this.closeBtn.on("click", this.closeClicked, this);
52774     this.closeBtn.hide();
52775
52776     this.createBody(config);
52777     this.visible = true;
52778     this.collapsed = false;
52779
52780     if(config.hideWhenEmpty){
52781         this.hide();
52782         this.on("paneladded", this.validateVisibility, this);
52783         this.on("panelremoved", this.validateVisibility, this);
52784     }
52785     this.applyConfig(config);
52786 };
52787
52788 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
52789
52790     createBody : function(){
52791         /** This region's body element 
52792         * @type Roo.Element */
52793         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
52794     },
52795
52796     applyConfig : function(c){
52797         if(c.collapsible && this.position != "center" && !this.collapsedEl){
52798             var dh = Roo.DomHelper;
52799             if(c.titlebar !== false){
52800                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
52801                 this.collapseBtn.on("click", this.collapse, this);
52802                 this.collapseBtn.enableDisplayMode();
52803
52804                 if(c.showPin === true || this.showPin){
52805                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
52806                     this.stickBtn.enableDisplayMode();
52807                     this.stickBtn.on("click", this.expand, this);
52808                     this.stickBtn.hide();
52809                 }
52810             }
52811             /** This region's collapsed element
52812             * @type Roo.Element */
52813             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
52814                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
52815             ]}, true);
52816             if(c.floatable !== false){
52817                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
52818                this.collapsedEl.on("click", this.collapseClick, this);
52819             }
52820
52821             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
52822                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
52823                    id: "message", unselectable: "on", style:{"float":"left"}});
52824                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
52825              }
52826             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
52827             this.expandBtn.on("click", this.expand, this);
52828         }
52829         if(this.collapseBtn){
52830             this.collapseBtn.setVisible(c.collapsible == true);
52831         }
52832         this.cmargins = c.cmargins || this.cmargins ||
52833                          (this.position == "west" || this.position == "east" ?
52834                              {top: 0, left: 2, right:2, bottom: 0} :
52835                              {top: 2, left: 0, right:0, bottom: 2});
52836         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52837         this.bottomTabs = c.tabPosition != "top";
52838         this.autoScroll = c.autoScroll || false;
52839         if(this.autoScroll){
52840             this.bodyEl.setStyle("overflow", "auto");
52841         }else{
52842             this.bodyEl.setStyle("overflow", "hidden");
52843         }
52844         //if(c.titlebar !== false){
52845             if((!c.titlebar && !c.title) || c.titlebar === false){
52846                 this.titleEl.hide();
52847             }else{
52848                 this.titleEl.show();
52849                 if(c.title){
52850                     this.titleTextEl.innerHTML = c.title;
52851                 }
52852             }
52853         //}
52854         this.duration = c.duration || .30;
52855         this.slideDuration = c.slideDuration || .45;
52856         this.config = c;
52857         if(c.collapsed){
52858             this.collapse(true);
52859         }
52860         if(c.hidden){
52861             this.hide();
52862         }
52863     },
52864     /**
52865      * Returns true if this region is currently visible.
52866      * @return {Boolean}
52867      */
52868     isVisible : function(){
52869         return this.visible;
52870     },
52871
52872     /**
52873      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
52874      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52875      */
52876     setCollapsedTitle : function(title){
52877         title = title || "&#160;";
52878         if(this.collapsedTitleTextEl){
52879             this.collapsedTitleTextEl.innerHTML = title;
52880         }
52881     },
52882
52883     getBox : function(){
52884         var b;
52885         if(!this.collapsed){
52886             b = this.el.getBox(false, true);
52887         }else{
52888             b = this.collapsedEl.getBox(false, true);
52889         }
52890         return b;
52891     },
52892
52893     getMargins : function(){
52894         return this.collapsed ? this.cmargins : this.margins;
52895     },
52896
52897     highlight : function(){
52898         this.el.addClass("x-layout-panel-dragover");
52899     },
52900
52901     unhighlight : function(){
52902         this.el.removeClass("x-layout-panel-dragover");
52903     },
52904
52905     updateBox : function(box){
52906         this.box = box;
52907         if(!this.collapsed){
52908             this.el.dom.style.left = box.x + "px";
52909             this.el.dom.style.top = box.y + "px";
52910             this.updateBody(box.width, box.height);
52911         }else{
52912             this.collapsedEl.dom.style.left = box.x + "px";
52913             this.collapsedEl.dom.style.top = box.y + "px";
52914             this.collapsedEl.setSize(box.width, box.height);
52915         }
52916         if(this.tabs){
52917             this.tabs.autoSizeTabs();
52918         }
52919     },
52920
52921     updateBody : function(w, h){
52922         if(w !== null){
52923             this.el.setWidth(w);
52924             w -= this.el.getBorderWidth("rl");
52925             if(this.config.adjustments){
52926                 w += this.config.adjustments[0];
52927             }
52928         }
52929         if(h !== null){
52930             this.el.setHeight(h);
52931             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52932             h -= this.el.getBorderWidth("tb");
52933             if(this.config.adjustments){
52934                 h += this.config.adjustments[1];
52935             }
52936             this.bodyEl.setHeight(h);
52937             if(this.tabs){
52938                 h = this.tabs.syncHeight(h);
52939             }
52940         }
52941         if(this.panelSize){
52942             w = w !== null ? w : this.panelSize.width;
52943             h = h !== null ? h : this.panelSize.height;
52944         }
52945         if(this.activePanel){
52946             var el = this.activePanel.getEl();
52947             w = w !== null ? w : el.getWidth();
52948             h = h !== null ? h : el.getHeight();
52949             this.panelSize = {width: w, height: h};
52950             this.activePanel.setSize(w, h);
52951         }
52952         if(Roo.isIE && this.tabs){
52953             this.tabs.el.repaint();
52954         }
52955     },
52956
52957     /**
52958      * Returns the container element for this region.
52959      * @return {Roo.Element}
52960      */
52961     getEl : function(){
52962         return this.el;
52963     },
52964
52965     /**
52966      * Hides this region.
52967      */
52968     hide : function(){
52969         if(!this.collapsed){
52970             this.el.dom.style.left = "-2000px";
52971             this.el.hide();
52972         }else{
52973             this.collapsedEl.dom.style.left = "-2000px";
52974             this.collapsedEl.hide();
52975         }
52976         this.visible = false;
52977         this.fireEvent("visibilitychange", this, false);
52978     },
52979
52980     /**
52981      * Shows this region if it was previously hidden.
52982      */
52983     show : function(){
52984         if(!this.collapsed){
52985             this.el.show();
52986         }else{
52987             this.collapsedEl.show();
52988         }
52989         this.visible = true;
52990         this.fireEvent("visibilitychange", this, true);
52991     },
52992
52993     closeClicked : function(){
52994         if(this.activePanel){
52995             this.remove(this.activePanel);
52996         }
52997     },
52998
52999     collapseClick : function(e){
53000         if(this.isSlid){
53001            e.stopPropagation();
53002            this.slideIn();
53003         }else{
53004            e.stopPropagation();
53005            this.slideOut();
53006         }
53007     },
53008
53009     /**
53010      * Collapses this region.
53011      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53012      */
53013     collapse : function(skipAnim, skipCheck){
53014         if(this.collapsed) {
53015             return;
53016         }
53017         
53018         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53019             
53020             this.collapsed = true;
53021             if(this.split){
53022                 this.split.el.hide();
53023             }
53024             if(this.config.animate && skipAnim !== true){
53025                 this.fireEvent("invalidated", this);
53026                 this.animateCollapse();
53027             }else{
53028                 this.el.setLocation(-20000,-20000);
53029                 this.el.hide();
53030                 this.collapsedEl.show();
53031                 this.fireEvent("collapsed", this);
53032                 this.fireEvent("invalidated", this);
53033             }
53034         }
53035         
53036     },
53037
53038     animateCollapse : function(){
53039         // overridden
53040     },
53041
53042     /**
53043      * Expands this region if it was previously collapsed.
53044      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53045      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53046      */
53047     expand : function(e, skipAnim){
53048         if(e) {
53049             e.stopPropagation();
53050         }
53051         if(!this.collapsed || this.el.hasActiveFx()) {
53052             return;
53053         }
53054         if(this.isSlid){
53055             this.afterSlideIn();
53056             skipAnim = true;
53057         }
53058         this.collapsed = false;
53059         if(this.config.animate && skipAnim !== true){
53060             this.animateExpand();
53061         }else{
53062             this.el.show();
53063             if(this.split){
53064                 this.split.el.show();
53065             }
53066             this.collapsedEl.setLocation(-2000,-2000);
53067             this.collapsedEl.hide();
53068             this.fireEvent("invalidated", this);
53069             this.fireEvent("expanded", this);
53070         }
53071     },
53072
53073     animateExpand : function(){
53074         // overridden
53075     },
53076
53077     initTabs : function()
53078     {
53079         this.bodyEl.setStyle("overflow", "hidden");
53080         var ts = new Roo.TabPanel(
53081                 this.bodyEl.dom,
53082                 {
53083                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53084                     disableTooltips: this.config.disableTabTips,
53085                     toolbar : this.config.toolbar
53086                 }
53087         );
53088         if(this.config.hideTabs){
53089             ts.stripWrap.setDisplayed(false);
53090         }
53091         this.tabs = ts;
53092         ts.resizeTabs = this.config.resizeTabs === true;
53093         ts.minTabWidth = this.config.minTabWidth || 40;
53094         ts.maxTabWidth = this.config.maxTabWidth || 250;
53095         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53096         ts.monitorResize = false;
53097         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53098         ts.bodyEl.addClass('x-layout-tabs-body');
53099         this.panels.each(this.initPanelAsTab, this);
53100     },
53101
53102     initPanelAsTab : function(panel){
53103         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53104                     this.config.closeOnTab && panel.isClosable());
53105         if(panel.tabTip !== undefined){
53106             ti.setTooltip(panel.tabTip);
53107         }
53108         ti.on("activate", function(){
53109               this.setActivePanel(panel);
53110         }, this);
53111         if(this.config.closeOnTab){
53112             ti.on("beforeclose", function(t, e){
53113                 e.cancel = true;
53114                 this.remove(panel);
53115             }, this);
53116         }
53117         return ti;
53118     },
53119
53120     updatePanelTitle : function(panel, title){
53121         if(this.activePanel == panel){
53122             this.updateTitle(title);
53123         }
53124         if(this.tabs){
53125             var ti = this.tabs.getTab(panel.getEl().id);
53126             ti.setText(title);
53127             if(panel.tabTip !== undefined){
53128                 ti.setTooltip(panel.tabTip);
53129             }
53130         }
53131     },
53132
53133     updateTitle : function(title){
53134         if(this.titleTextEl && !this.config.title){
53135             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53136         }
53137     },
53138
53139     setActivePanel : function(panel){
53140         panel = this.getPanel(panel);
53141         if(this.activePanel && this.activePanel != panel){
53142             this.activePanel.setActiveState(false);
53143         }
53144         this.activePanel = panel;
53145         panel.setActiveState(true);
53146         if(this.panelSize){
53147             panel.setSize(this.panelSize.width, this.panelSize.height);
53148         }
53149         if(this.closeBtn){
53150             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53151         }
53152         this.updateTitle(panel.getTitle());
53153         if(this.tabs){
53154             this.fireEvent("invalidated", this);
53155         }
53156         this.fireEvent("panelactivated", this, panel);
53157     },
53158
53159     /**
53160      * Shows the specified panel.
53161      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53162      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53163      */
53164     showPanel : function(panel)
53165     {
53166         panel = this.getPanel(panel);
53167         if(panel){
53168             if(this.tabs){
53169                 var tab = this.tabs.getTab(panel.getEl().id);
53170                 if(tab.isHidden()){
53171                     this.tabs.unhideTab(tab.id);
53172                 }
53173                 tab.activate();
53174             }else{
53175                 this.setActivePanel(panel);
53176             }
53177         }
53178         return panel;
53179     },
53180
53181     /**
53182      * Get the active panel for this region.
53183      * @return {Roo.ContentPanel} The active panel or null
53184      */
53185     getActivePanel : function(){
53186         return this.activePanel;
53187     },
53188
53189     validateVisibility : function(){
53190         if(this.panels.getCount() < 1){
53191             this.updateTitle("&#160;");
53192             this.closeBtn.hide();
53193             this.hide();
53194         }else{
53195             if(!this.isVisible()){
53196                 this.show();
53197             }
53198         }
53199     },
53200
53201     /**
53202      * Adds the passed ContentPanel(s) to this region.
53203      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53204      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53205      */
53206     add : function(panel){
53207         if(arguments.length > 1){
53208             for(var i = 0, len = arguments.length; i < len; i++) {
53209                 this.add(arguments[i]);
53210             }
53211             return null;
53212         }
53213         if(this.hasPanel(panel)){
53214             this.showPanel(panel);
53215             return panel;
53216         }
53217         panel.setRegion(this);
53218         this.panels.add(panel);
53219         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53220             this.bodyEl.dom.appendChild(panel.getEl().dom);
53221             if(panel.background !== true){
53222                 this.setActivePanel(panel);
53223             }
53224             this.fireEvent("paneladded", this, panel);
53225             return panel;
53226         }
53227         if(!this.tabs){
53228             this.initTabs();
53229         }else{
53230             this.initPanelAsTab(panel);
53231         }
53232         if(panel.background !== true){
53233             this.tabs.activate(panel.getEl().id);
53234         }
53235         this.fireEvent("paneladded", this, panel);
53236         return panel;
53237     },
53238
53239     /**
53240      * Hides the tab for the specified panel.
53241      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53242      */
53243     hidePanel : function(panel){
53244         if(this.tabs && (panel = this.getPanel(panel))){
53245             this.tabs.hideTab(panel.getEl().id);
53246         }
53247     },
53248
53249     /**
53250      * Unhides the tab for a previously hidden panel.
53251      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53252      */
53253     unhidePanel : function(panel){
53254         if(this.tabs && (panel = this.getPanel(panel))){
53255             this.tabs.unhideTab(panel.getEl().id);
53256         }
53257     },
53258
53259     clearPanels : function(){
53260         while(this.panels.getCount() > 0){
53261              this.remove(this.panels.first());
53262         }
53263     },
53264
53265     /**
53266      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53267      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53268      * @param {Boolean} preservePanel Overrides the config preservePanel option
53269      * @return {Roo.ContentPanel} The panel that was removed
53270      */
53271     remove : function(panel, preservePanel){
53272         panel = this.getPanel(panel);
53273         if(!panel){
53274             return null;
53275         }
53276         var e = {};
53277         this.fireEvent("beforeremove", this, panel, e);
53278         if(e.cancel === true){
53279             return null;
53280         }
53281         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53282         var panelId = panel.getId();
53283         this.panels.removeKey(panelId);
53284         if(preservePanel){
53285             document.body.appendChild(panel.getEl().dom);
53286         }
53287         if(this.tabs){
53288             this.tabs.removeTab(panel.getEl().id);
53289         }else if (!preservePanel){
53290             this.bodyEl.dom.removeChild(panel.getEl().dom);
53291         }
53292         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53293             var p = this.panels.first();
53294             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53295             tempEl.appendChild(p.getEl().dom);
53296             this.bodyEl.update("");
53297             this.bodyEl.dom.appendChild(p.getEl().dom);
53298             tempEl = null;
53299             this.updateTitle(p.getTitle());
53300             this.tabs = null;
53301             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53302             this.setActivePanel(p);
53303         }
53304         panel.setRegion(null);
53305         if(this.activePanel == panel){
53306             this.activePanel = null;
53307         }
53308         if(this.config.autoDestroy !== false && preservePanel !== true){
53309             try{panel.destroy();}catch(e){}
53310         }
53311         this.fireEvent("panelremoved", this, panel);
53312         return panel;
53313     },
53314
53315     /**
53316      * Returns the TabPanel component used by this region
53317      * @return {Roo.TabPanel}
53318      */
53319     getTabs : function(){
53320         return this.tabs;
53321     },
53322
53323     createTool : function(parentEl, className){
53324         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53325             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53326         btn.addClassOnOver("x-layout-tools-button-over");
53327         return btn;
53328     }
53329 });/*
53330  * Based on:
53331  * Ext JS Library 1.1.1
53332  * Copyright(c) 2006-2007, Ext JS, LLC.
53333  *
53334  * Originally Released Under LGPL - original licence link has changed is not relivant.
53335  *
53336  * Fork - LGPL
53337  * <script type="text/javascript">
53338  */
53339  
53340
53341
53342 /**
53343  * @class Roo.SplitLayoutRegion
53344  * @extends Roo.LayoutRegion
53345  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53346  */
53347 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53348     this.cursor = cursor;
53349     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53350 };
53351
53352 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53353     splitTip : "Drag to resize.",
53354     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53355     useSplitTips : false,
53356
53357     applyConfig : function(config){
53358         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53359         if(config.split){
53360             if(!this.split){
53361                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53362                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53363                 /** The SplitBar for this region 
53364                 * @type Roo.SplitBar */
53365                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53366                 this.split.on("moved", this.onSplitMove, this);
53367                 this.split.useShim = config.useShim === true;
53368                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53369                 if(this.useSplitTips){
53370                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53371                 }
53372                 if(config.collapsible){
53373                     this.split.el.on("dblclick", this.collapse,  this);
53374                 }
53375             }
53376             if(typeof config.minSize != "undefined"){
53377                 this.split.minSize = config.minSize;
53378             }
53379             if(typeof config.maxSize != "undefined"){
53380                 this.split.maxSize = config.maxSize;
53381             }
53382             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53383                 this.hideSplitter();
53384             }
53385         }
53386     },
53387
53388     getHMaxSize : function(){
53389          var cmax = this.config.maxSize || 10000;
53390          var center = this.mgr.getRegion("center");
53391          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53392     },
53393
53394     getVMaxSize : function(){
53395          var cmax = this.config.maxSize || 10000;
53396          var center = this.mgr.getRegion("center");
53397          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53398     },
53399
53400     onSplitMove : function(split, newSize){
53401         this.fireEvent("resized", this, newSize);
53402     },
53403     
53404     /** 
53405      * Returns the {@link Roo.SplitBar} for this region.
53406      * @return {Roo.SplitBar}
53407      */
53408     getSplitBar : function(){
53409         return this.split;
53410     },
53411     
53412     hide : function(){
53413         this.hideSplitter();
53414         Roo.SplitLayoutRegion.superclass.hide.call(this);
53415     },
53416
53417     hideSplitter : function(){
53418         if(this.split){
53419             this.split.el.setLocation(-2000,-2000);
53420             this.split.el.hide();
53421         }
53422     },
53423
53424     show : function(){
53425         if(this.split){
53426             this.split.el.show();
53427         }
53428         Roo.SplitLayoutRegion.superclass.show.call(this);
53429     },
53430     
53431     beforeSlide: function(){
53432         if(Roo.isGecko){// firefox overflow auto bug workaround
53433             this.bodyEl.clip();
53434             if(this.tabs) {
53435                 this.tabs.bodyEl.clip();
53436             }
53437             if(this.activePanel){
53438                 this.activePanel.getEl().clip();
53439                 
53440                 if(this.activePanel.beforeSlide){
53441                     this.activePanel.beforeSlide();
53442                 }
53443             }
53444         }
53445     },
53446     
53447     afterSlide : function(){
53448         if(Roo.isGecko){// firefox overflow auto bug workaround
53449             this.bodyEl.unclip();
53450             if(this.tabs) {
53451                 this.tabs.bodyEl.unclip();
53452             }
53453             if(this.activePanel){
53454                 this.activePanel.getEl().unclip();
53455                 if(this.activePanel.afterSlide){
53456                     this.activePanel.afterSlide();
53457                 }
53458             }
53459         }
53460     },
53461
53462     initAutoHide : function(){
53463         if(this.autoHide !== false){
53464             if(!this.autoHideHd){
53465                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53466                 this.autoHideHd = {
53467                     "mouseout": function(e){
53468                         if(!e.within(this.el, true)){
53469                             st.delay(500);
53470                         }
53471                     },
53472                     "mouseover" : function(e){
53473                         st.cancel();
53474                     },
53475                     scope : this
53476                 };
53477             }
53478             this.el.on(this.autoHideHd);
53479         }
53480     },
53481
53482     clearAutoHide : function(){
53483         if(this.autoHide !== false){
53484             this.el.un("mouseout", this.autoHideHd.mouseout);
53485             this.el.un("mouseover", this.autoHideHd.mouseover);
53486         }
53487     },
53488
53489     clearMonitor : function(){
53490         Roo.get(document).un("click", this.slideInIf, this);
53491     },
53492
53493     // these names are backwards but not changed for compat
53494     slideOut : function(){
53495         if(this.isSlid || this.el.hasActiveFx()){
53496             return;
53497         }
53498         this.isSlid = true;
53499         if(this.collapseBtn){
53500             this.collapseBtn.hide();
53501         }
53502         this.closeBtnState = this.closeBtn.getStyle('display');
53503         this.closeBtn.hide();
53504         if(this.stickBtn){
53505             this.stickBtn.show();
53506         }
53507         this.el.show();
53508         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53509         this.beforeSlide();
53510         this.el.setStyle("z-index", 10001);
53511         this.el.slideIn(this.getSlideAnchor(), {
53512             callback: function(){
53513                 this.afterSlide();
53514                 this.initAutoHide();
53515                 Roo.get(document).on("click", this.slideInIf, this);
53516                 this.fireEvent("slideshow", this);
53517             },
53518             scope: this,
53519             block: true
53520         });
53521     },
53522
53523     afterSlideIn : function(){
53524         this.clearAutoHide();
53525         this.isSlid = false;
53526         this.clearMonitor();
53527         this.el.setStyle("z-index", "");
53528         if(this.collapseBtn){
53529             this.collapseBtn.show();
53530         }
53531         this.closeBtn.setStyle('display', this.closeBtnState);
53532         if(this.stickBtn){
53533             this.stickBtn.hide();
53534         }
53535         this.fireEvent("slidehide", this);
53536     },
53537
53538     slideIn : function(cb){
53539         if(!this.isSlid || this.el.hasActiveFx()){
53540             Roo.callback(cb);
53541             return;
53542         }
53543         this.isSlid = false;
53544         this.beforeSlide();
53545         this.el.slideOut(this.getSlideAnchor(), {
53546             callback: function(){
53547                 this.el.setLeftTop(-10000, -10000);
53548                 this.afterSlide();
53549                 this.afterSlideIn();
53550                 Roo.callback(cb);
53551             },
53552             scope: this,
53553             block: true
53554         });
53555     },
53556     
53557     slideInIf : function(e){
53558         if(!e.within(this.el)){
53559             this.slideIn();
53560         }
53561     },
53562
53563     animateCollapse : function(){
53564         this.beforeSlide();
53565         this.el.setStyle("z-index", 20000);
53566         var anchor = this.getSlideAnchor();
53567         this.el.slideOut(anchor, {
53568             callback : function(){
53569                 this.el.setStyle("z-index", "");
53570                 this.collapsedEl.slideIn(anchor, {duration:.3});
53571                 this.afterSlide();
53572                 this.el.setLocation(-10000,-10000);
53573                 this.el.hide();
53574                 this.fireEvent("collapsed", this);
53575             },
53576             scope: this,
53577             block: true
53578         });
53579     },
53580
53581     animateExpand : function(){
53582         this.beforeSlide();
53583         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53584         this.el.setStyle("z-index", 20000);
53585         this.collapsedEl.hide({
53586             duration:.1
53587         });
53588         this.el.slideIn(this.getSlideAnchor(), {
53589             callback : function(){
53590                 this.el.setStyle("z-index", "");
53591                 this.afterSlide();
53592                 if(this.split){
53593                     this.split.el.show();
53594                 }
53595                 this.fireEvent("invalidated", this);
53596                 this.fireEvent("expanded", this);
53597             },
53598             scope: this,
53599             block: true
53600         });
53601     },
53602
53603     anchors : {
53604         "west" : "left",
53605         "east" : "right",
53606         "north" : "top",
53607         "south" : "bottom"
53608     },
53609
53610     sanchors : {
53611         "west" : "l",
53612         "east" : "r",
53613         "north" : "t",
53614         "south" : "b"
53615     },
53616
53617     canchors : {
53618         "west" : "tl-tr",
53619         "east" : "tr-tl",
53620         "north" : "tl-bl",
53621         "south" : "bl-tl"
53622     },
53623
53624     getAnchor : function(){
53625         return this.anchors[this.position];
53626     },
53627
53628     getCollapseAnchor : function(){
53629         return this.canchors[this.position];
53630     },
53631
53632     getSlideAnchor : function(){
53633         return this.sanchors[this.position];
53634     },
53635
53636     getAlignAdj : function(){
53637         var cm = this.cmargins;
53638         switch(this.position){
53639             case "west":
53640                 return [0, 0];
53641             break;
53642             case "east":
53643                 return [0, 0];
53644             break;
53645             case "north":
53646                 return [0, 0];
53647             break;
53648             case "south":
53649                 return [0, 0];
53650             break;
53651         }
53652     },
53653
53654     getExpandAdj : function(){
53655         var c = this.collapsedEl, cm = this.cmargins;
53656         switch(this.position){
53657             case "west":
53658                 return [-(cm.right+c.getWidth()+cm.left), 0];
53659             break;
53660             case "east":
53661                 return [cm.right+c.getWidth()+cm.left, 0];
53662             break;
53663             case "north":
53664                 return [0, -(cm.top+cm.bottom+c.getHeight())];
53665             break;
53666             case "south":
53667                 return [0, cm.top+cm.bottom+c.getHeight()];
53668             break;
53669         }
53670     }
53671 });/*
53672  * Based on:
53673  * Ext JS Library 1.1.1
53674  * Copyright(c) 2006-2007, Ext JS, LLC.
53675  *
53676  * Originally Released Under LGPL - original licence link has changed is not relivant.
53677  *
53678  * Fork - LGPL
53679  * <script type="text/javascript">
53680  */
53681 /*
53682  * These classes are private internal classes
53683  */
53684 Roo.CenterLayoutRegion = function(mgr, config){
53685     Roo.LayoutRegion.call(this, mgr, config, "center");
53686     this.visible = true;
53687     this.minWidth = config.minWidth || 20;
53688     this.minHeight = config.minHeight || 20;
53689 };
53690
53691 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
53692     hide : function(){
53693         // center panel can't be hidden
53694     },
53695     
53696     show : function(){
53697         // center panel can't be hidden
53698     },
53699     
53700     getMinWidth: function(){
53701         return this.minWidth;
53702     },
53703     
53704     getMinHeight: function(){
53705         return this.minHeight;
53706     }
53707 });
53708
53709
53710 Roo.NorthLayoutRegion = function(mgr, config){
53711     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
53712     if(this.split){
53713         this.split.placement = Roo.SplitBar.TOP;
53714         this.split.orientation = Roo.SplitBar.VERTICAL;
53715         this.split.el.addClass("x-layout-split-v");
53716     }
53717     var size = config.initialSize || config.height;
53718     if(typeof size != "undefined"){
53719         this.el.setHeight(size);
53720     }
53721 };
53722 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
53723     orientation: Roo.SplitBar.VERTICAL,
53724     getBox : function(){
53725         if(this.collapsed){
53726             return this.collapsedEl.getBox();
53727         }
53728         var box = this.el.getBox();
53729         if(this.split){
53730             box.height += this.split.el.getHeight();
53731         }
53732         return box;
53733     },
53734     
53735     updateBox : function(box){
53736         if(this.split && !this.collapsed){
53737             box.height -= this.split.el.getHeight();
53738             this.split.el.setLeft(box.x);
53739             this.split.el.setTop(box.y+box.height);
53740             this.split.el.setWidth(box.width);
53741         }
53742         if(this.collapsed){
53743             this.updateBody(box.width, null);
53744         }
53745         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53746     }
53747 });
53748
53749 Roo.SouthLayoutRegion = function(mgr, config){
53750     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
53751     if(this.split){
53752         this.split.placement = Roo.SplitBar.BOTTOM;
53753         this.split.orientation = Roo.SplitBar.VERTICAL;
53754         this.split.el.addClass("x-layout-split-v");
53755     }
53756     var size = config.initialSize || config.height;
53757     if(typeof size != "undefined"){
53758         this.el.setHeight(size);
53759     }
53760 };
53761 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
53762     orientation: Roo.SplitBar.VERTICAL,
53763     getBox : function(){
53764         if(this.collapsed){
53765             return this.collapsedEl.getBox();
53766         }
53767         var box = this.el.getBox();
53768         if(this.split){
53769             var sh = this.split.el.getHeight();
53770             box.height += sh;
53771             box.y -= sh;
53772         }
53773         return box;
53774     },
53775     
53776     updateBox : function(box){
53777         if(this.split && !this.collapsed){
53778             var sh = this.split.el.getHeight();
53779             box.height -= sh;
53780             box.y += sh;
53781             this.split.el.setLeft(box.x);
53782             this.split.el.setTop(box.y-sh);
53783             this.split.el.setWidth(box.width);
53784         }
53785         if(this.collapsed){
53786             this.updateBody(box.width, null);
53787         }
53788         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53789     }
53790 });
53791
53792 Roo.EastLayoutRegion = function(mgr, config){
53793     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
53794     if(this.split){
53795         this.split.placement = Roo.SplitBar.RIGHT;
53796         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53797         this.split.el.addClass("x-layout-split-h");
53798     }
53799     var size = config.initialSize || config.width;
53800     if(typeof size != "undefined"){
53801         this.el.setWidth(size);
53802     }
53803 };
53804 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
53805     orientation: Roo.SplitBar.HORIZONTAL,
53806     getBox : function(){
53807         if(this.collapsed){
53808             return this.collapsedEl.getBox();
53809         }
53810         var box = this.el.getBox();
53811         if(this.split){
53812             var sw = this.split.el.getWidth();
53813             box.width += sw;
53814             box.x -= sw;
53815         }
53816         return box;
53817     },
53818
53819     updateBox : function(box){
53820         if(this.split && !this.collapsed){
53821             var sw = this.split.el.getWidth();
53822             box.width -= sw;
53823             this.split.el.setLeft(box.x);
53824             this.split.el.setTop(box.y);
53825             this.split.el.setHeight(box.height);
53826             box.x += sw;
53827         }
53828         if(this.collapsed){
53829             this.updateBody(null, box.height);
53830         }
53831         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53832     }
53833 });
53834
53835 Roo.WestLayoutRegion = function(mgr, config){
53836     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
53837     if(this.split){
53838         this.split.placement = Roo.SplitBar.LEFT;
53839         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53840         this.split.el.addClass("x-layout-split-h");
53841     }
53842     var size = config.initialSize || config.width;
53843     if(typeof size != "undefined"){
53844         this.el.setWidth(size);
53845     }
53846 };
53847 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
53848     orientation: Roo.SplitBar.HORIZONTAL,
53849     getBox : function(){
53850         if(this.collapsed){
53851             return this.collapsedEl.getBox();
53852         }
53853         var box = this.el.getBox();
53854         if(this.split){
53855             box.width += this.split.el.getWidth();
53856         }
53857         return box;
53858     },
53859     
53860     updateBox : function(box){
53861         if(this.split && !this.collapsed){
53862             var sw = this.split.el.getWidth();
53863             box.width -= sw;
53864             this.split.el.setLeft(box.x+box.width);
53865             this.split.el.setTop(box.y);
53866             this.split.el.setHeight(box.height);
53867         }
53868         if(this.collapsed){
53869             this.updateBody(null, box.height);
53870         }
53871         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53872     }
53873 });
53874 /*
53875  * Based on:
53876  * Ext JS Library 1.1.1
53877  * Copyright(c) 2006-2007, Ext JS, LLC.
53878  *
53879  * Originally Released Under LGPL - original licence link has changed is not relivant.
53880  *
53881  * Fork - LGPL
53882  * <script type="text/javascript">
53883  */
53884  
53885  
53886 /*
53887  * Private internal class for reading and applying state
53888  */
53889 Roo.LayoutStateManager = function(layout){
53890      // default empty state
53891      this.state = {
53892         north: {},
53893         south: {},
53894         east: {},
53895         west: {}       
53896     };
53897 };
53898
53899 Roo.LayoutStateManager.prototype = {
53900     init : function(layout, provider){
53901         this.provider = provider;
53902         var state = provider.get(layout.id+"-layout-state");
53903         if(state){
53904             var wasUpdating = layout.isUpdating();
53905             if(!wasUpdating){
53906                 layout.beginUpdate();
53907             }
53908             for(var key in state){
53909                 if(typeof state[key] != "function"){
53910                     var rstate = state[key];
53911                     var r = layout.getRegion(key);
53912                     if(r && rstate){
53913                         if(rstate.size){
53914                             r.resizeTo(rstate.size);
53915                         }
53916                         if(rstate.collapsed == true){
53917                             r.collapse(true);
53918                         }else{
53919                             r.expand(null, true);
53920                         }
53921                     }
53922                 }
53923             }
53924             if(!wasUpdating){
53925                 layout.endUpdate();
53926             }
53927             this.state = state; 
53928         }
53929         this.layout = layout;
53930         layout.on("regionresized", this.onRegionResized, this);
53931         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53932         layout.on("regionexpanded", this.onRegionExpanded, this);
53933     },
53934     
53935     storeState : function(){
53936         this.provider.set(this.layout.id+"-layout-state", this.state);
53937     },
53938     
53939     onRegionResized : function(region, newSize){
53940         this.state[region.getPosition()].size = newSize;
53941         this.storeState();
53942     },
53943     
53944     onRegionCollapsed : function(region){
53945         this.state[region.getPosition()].collapsed = true;
53946         this.storeState();
53947     },
53948     
53949     onRegionExpanded : function(region){
53950         this.state[region.getPosition()].collapsed = false;
53951         this.storeState();
53952     }
53953 };/*
53954  * Based on:
53955  * Ext JS Library 1.1.1
53956  * Copyright(c) 2006-2007, Ext JS, LLC.
53957  *
53958  * Originally Released Under LGPL - original licence link has changed is not relivant.
53959  *
53960  * Fork - LGPL
53961  * <script type="text/javascript">
53962  */
53963 /**
53964  * @class Roo.ContentPanel
53965  * @extends Roo.util.Observable
53966  * A basic ContentPanel element.
53967  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53968  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53969  * @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
53970  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53971  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53972  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53973  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53974  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53975  * @cfg {String} title          The title for this panel
53976  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53977  * @cfg {String} url            Calls {@link #setUrl} with this value
53978  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53979  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53980  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53981  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53982
53983  * @constructor
53984  * Create a new ContentPanel.
53985  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53986  * @param {String/Object} config A string to set only the title or a config object
53987  * @param {String} content (optional) Set the HTML content for this panel
53988  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53989  */
53990 Roo.ContentPanel = function(el, config, content){
53991     
53992      
53993     /*
53994     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53995         config = el;
53996         el = Roo.id();
53997     }
53998     if (config && config.parentLayout) { 
53999         el = config.parentLayout.el.createChild(); 
54000     }
54001     */
54002     if(el.autoCreate){ // xtype is available if this is called from factory
54003         config = el;
54004         el = Roo.id();
54005     }
54006     this.el = Roo.get(el);
54007     if(!this.el && config && config.autoCreate){
54008         if(typeof config.autoCreate == "object"){
54009             if(!config.autoCreate.id){
54010                 config.autoCreate.id = config.id||el;
54011             }
54012             this.el = Roo.DomHelper.append(document.body,
54013                         config.autoCreate, true);
54014         }else{
54015             this.el = Roo.DomHelper.append(document.body,
54016                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54017         }
54018     }
54019     this.closable = false;
54020     this.loaded = false;
54021     this.active = false;
54022     if(typeof config == "string"){
54023         this.title = config;
54024     }else{
54025         Roo.apply(this, config);
54026     }
54027     
54028     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54029         this.wrapEl = this.el.wrap();
54030         this.toolbar.container = this.el.insertSibling(false, 'before');
54031         this.toolbar = new Roo.Toolbar(this.toolbar);
54032     }
54033     
54034     // xtype created footer. - not sure if will work as we normally have to render first..
54035     if (this.footer && !this.footer.el && this.footer.xtype) {
54036         if (!this.wrapEl) {
54037             this.wrapEl = this.el.wrap();
54038         }
54039     
54040         this.footer.container = this.wrapEl.createChild();
54041          
54042         this.footer = Roo.factory(this.footer, Roo);
54043         
54044     }
54045     
54046     if(this.resizeEl){
54047         this.resizeEl = Roo.get(this.resizeEl, true);
54048     }else{
54049         this.resizeEl = this.el;
54050     }
54051     // handle view.xtype
54052     
54053  
54054     
54055     
54056     this.addEvents({
54057         /**
54058          * @event activate
54059          * Fires when this panel is activated. 
54060          * @param {Roo.ContentPanel} this
54061          */
54062         "activate" : true,
54063         /**
54064          * @event deactivate
54065          * Fires when this panel is activated. 
54066          * @param {Roo.ContentPanel} this
54067          */
54068         "deactivate" : true,
54069
54070         /**
54071          * @event resize
54072          * Fires when this panel is resized if fitToFrame is true.
54073          * @param {Roo.ContentPanel} this
54074          * @param {Number} width The width after any component adjustments
54075          * @param {Number} height The height after any component adjustments
54076          */
54077         "resize" : true,
54078         
54079          /**
54080          * @event render
54081          * Fires when this tab is created
54082          * @param {Roo.ContentPanel} this
54083          */
54084         "render" : true
54085          
54086         
54087     });
54088     
54089
54090     
54091     
54092     if(this.autoScroll){
54093         this.resizeEl.setStyle("overflow", "auto");
54094     } else {
54095         // fix randome scrolling
54096         this.el.on('scroll', function() {
54097             Roo.log('fix random scolling');
54098             this.scrollTo('top',0); 
54099         });
54100     }
54101     content = content || this.content;
54102     if(content){
54103         this.setContent(content);
54104     }
54105     if(config && config.url){
54106         this.setUrl(this.url, this.params, this.loadOnce);
54107     }
54108     
54109     
54110     
54111     Roo.ContentPanel.superclass.constructor.call(this);
54112     
54113     if (this.view && typeof(this.view.xtype) != 'undefined') {
54114         this.view.el = this.el.appendChild(document.createElement("div"));
54115         this.view = Roo.factory(this.view); 
54116         this.view.render  &&  this.view.render(false, '');  
54117     }
54118     
54119     
54120     this.fireEvent('render', this);
54121 };
54122
54123 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54124     tabTip:'',
54125     setRegion : function(region){
54126         this.region = region;
54127         if(region){
54128            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54129         }else{
54130            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54131         } 
54132     },
54133     
54134     /**
54135      * Returns the toolbar for this Panel if one was configured. 
54136      * @return {Roo.Toolbar} 
54137      */
54138     getToolbar : function(){
54139         return this.toolbar;
54140     },
54141     
54142     setActiveState : function(active){
54143         this.active = active;
54144         if(!active){
54145             this.fireEvent("deactivate", this);
54146         }else{
54147             this.fireEvent("activate", this);
54148         }
54149     },
54150     /**
54151      * Updates this panel's element
54152      * @param {String} content The new content
54153      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54154     */
54155     setContent : function(content, loadScripts){
54156         this.el.update(content, loadScripts);
54157     },
54158
54159     ignoreResize : function(w, h){
54160         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54161             return true;
54162         }else{
54163             this.lastSize = {width: w, height: h};
54164             return false;
54165         }
54166     },
54167     /**
54168      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54169      * @return {Roo.UpdateManager} The UpdateManager
54170      */
54171     getUpdateManager : function(){
54172         return this.el.getUpdateManager();
54173     },
54174      /**
54175      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54176      * @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:
54177 <pre><code>
54178 panel.load({
54179     url: "your-url.php",
54180     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54181     callback: yourFunction,
54182     scope: yourObject, //(optional scope)
54183     discardUrl: false,
54184     nocache: false,
54185     text: "Loading...",
54186     timeout: 30,
54187     scripts: false
54188 });
54189 </code></pre>
54190      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54191      * 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.
54192      * @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}
54193      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54194      * @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.
54195      * @return {Roo.ContentPanel} this
54196      */
54197     load : function(){
54198         var um = this.el.getUpdateManager();
54199         um.update.apply(um, arguments);
54200         return this;
54201     },
54202
54203
54204     /**
54205      * 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.
54206      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54207      * @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)
54208      * @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)
54209      * @return {Roo.UpdateManager} The UpdateManager
54210      */
54211     setUrl : function(url, params, loadOnce){
54212         if(this.refreshDelegate){
54213             this.removeListener("activate", this.refreshDelegate);
54214         }
54215         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54216         this.on("activate", this.refreshDelegate);
54217         return this.el.getUpdateManager();
54218     },
54219     
54220     _handleRefresh : function(url, params, loadOnce){
54221         if(!loadOnce || !this.loaded){
54222             var updater = this.el.getUpdateManager();
54223             updater.update(url, params, this._setLoaded.createDelegate(this));
54224         }
54225     },
54226     
54227     _setLoaded : function(){
54228         this.loaded = true;
54229     }, 
54230     
54231     /**
54232      * Returns this panel's id
54233      * @return {String} 
54234      */
54235     getId : function(){
54236         return this.el.id;
54237     },
54238     
54239     /** 
54240      * Returns this panel's element - used by regiosn to add.
54241      * @return {Roo.Element} 
54242      */
54243     getEl : function(){
54244         return this.wrapEl || this.el;
54245     },
54246     
54247     adjustForComponents : function(width, height)
54248     {
54249         //Roo.log('adjustForComponents ');
54250         if(this.resizeEl != this.el){
54251             width -= this.el.getFrameWidth('lr');
54252             height -= this.el.getFrameWidth('tb');
54253         }
54254         if(this.toolbar){
54255             var te = this.toolbar.getEl();
54256             height -= te.getHeight();
54257             te.setWidth(width);
54258         }
54259         if(this.footer){
54260             var te = this.footer.getEl();
54261             //Roo.log("footer:" + te.getHeight());
54262             
54263             height -= te.getHeight();
54264             te.setWidth(width);
54265         }
54266         
54267         
54268         if(this.adjustments){
54269             width += this.adjustments[0];
54270             height += this.adjustments[1];
54271         }
54272         return {"width": width, "height": height};
54273     },
54274     
54275     setSize : function(width, height){
54276         if(this.fitToFrame && !this.ignoreResize(width, height)){
54277             if(this.fitContainer && this.resizeEl != this.el){
54278                 this.el.setSize(width, height);
54279             }
54280             var size = this.adjustForComponents(width, height);
54281             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54282             this.fireEvent('resize', this, size.width, size.height);
54283         }
54284     },
54285     
54286     /**
54287      * Returns this panel's title
54288      * @return {String} 
54289      */
54290     getTitle : function(){
54291         return this.title;
54292     },
54293     
54294     /**
54295      * Set this panel's title
54296      * @param {String} title
54297      */
54298     setTitle : function(title){
54299         this.title = title;
54300         if(this.region){
54301             this.region.updatePanelTitle(this, title);
54302         }
54303     },
54304     
54305     /**
54306      * Returns true is this panel was configured to be closable
54307      * @return {Boolean} 
54308      */
54309     isClosable : function(){
54310         return this.closable;
54311     },
54312     
54313     beforeSlide : function(){
54314         this.el.clip();
54315         this.resizeEl.clip();
54316     },
54317     
54318     afterSlide : function(){
54319         this.el.unclip();
54320         this.resizeEl.unclip();
54321     },
54322     
54323     /**
54324      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54325      *   Will fail silently if the {@link #setUrl} method has not been called.
54326      *   This does not activate the panel, just updates its content.
54327      */
54328     refresh : function(){
54329         if(this.refreshDelegate){
54330            this.loaded = false;
54331            this.refreshDelegate();
54332         }
54333     },
54334     
54335     /**
54336      * Destroys this panel
54337      */
54338     destroy : function(){
54339         this.el.removeAllListeners();
54340         var tempEl = document.createElement("span");
54341         tempEl.appendChild(this.el.dom);
54342         tempEl.innerHTML = "";
54343         this.el.remove();
54344         this.el = null;
54345     },
54346     
54347     /**
54348      * form - if the content panel contains a form - this is a reference to it.
54349      * @type {Roo.form.Form}
54350      */
54351     form : false,
54352     /**
54353      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54354      *    This contains a reference to it.
54355      * @type {Roo.View}
54356      */
54357     view : false,
54358     
54359       /**
54360      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54361      * <pre><code>
54362
54363 layout.addxtype({
54364        xtype : 'Form',
54365        items: [ .... ]
54366    }
54367 );
54368
54369 </code></pre>
54370      * @param {Object} cfg Xtype definition of item to add.
54371      */
54372     
54373     addxtype : function(cfg) {
54374         // add form..
54375         if (cfg.xtype.match(/^Form$/)) {
54376             
54377             var el;
54378             //if (this.footer) {
54379             //    el = this.footer.container.insertSibling(false, 'before');
54380             //} else {
54381                 el = this.el.createChild();
54382             //}
54383
54384             this.form = new  Roo.form.Form(cfg);
54385             
54386             
54387             if ( this.form.allItems.length) {
54388                 this.form.render(el.dom);
54389             }
54390             return this.form;
54391         }
54392         // should only have one of theses..
54393         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54394             // views.. should not be just added - used named prop 'view''
54395             
54396             cfg.el = this.el.appendChild(document.createElement("div"));
54397             // factory?
54398             
54399             var ret = new Roo.factory(cfg);
54400              
54401              ret.render && ret.render(false, ''); // render blank..
54402             this.view = ret;
54403             return ret;
54404         }
54405         return false;
54406     }
54407 });
54408
54409 /**
54410  * @class Roo.GridPanel
54411  * @extends Roo.ContentPanel
54412  * @constructor
54413  * Create a new GridPanel.
54414  * @param {Roo.grid.Grid} grid The grid for this panel
54415  * @param {String/Object} config A string to set only the panel's title, or a config object
54416  */
54417 Roo.GridPanel = function(grid, config){
54418     
54419   
54420     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54421         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54422         
54423     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54424     
54425     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54426     
54427     if(this.toolbar){
54428         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54429     }
54430     // xtype created footer. - not sure if will work as we normally have to render first..
54431     if (this.footer && !this.footer.el && this.footer.xtype) {
54432         
54433         this.footer.container = this.grid.getView().getFooterPanel(true);
54434         this.footer.dataSource = this.grid.dataSource;
54435         this.footer = Roo.factory(this.footer, Roo);
54436         
54437     }
54438     
54439     grid.monitorWindowResize = false; // turn off autosizing
54440     grid.autoHeight = false;
54441     grid.autoWidth = false;
54442     this.grid = grid;
54443     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54444 };
54445
54446 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54447     getId : function(){
54448         return this.grid.id;
54449     },
54450     
54451     /**
54452      * Returns the grid for this panel
54453      * @return {Roo.grid.Grid} 
54454      */
54455     getGrid : function(){
54456         return this.grid;    
54457     },
54458     
54459     setSize : function(width, height){
54460         if(!this.ignoreResize(width, height)){
54461             var grid = this.grid;
54462             var size = this.adjustForComponents(width, height);
54463             grid.getGridEl().setSize(size.width, size.height);
54464             grid.autoSize();
54465         }
54466     },
54467     
54468     beforeSlide : function(){
54469         this.grid.getView().scroller.clip();
54470     },
54471     
54472     afterSlide : function(){
54473         this.grid.getView().scroller.unclip();
54474     },
54475     
54476     destroy : function(){
54477         this.grid.destroy();
54478         delete this.grid;
54479         Roo.GridPanel.superclass.destroy.call(this); 
54480     }
54481 });
54482
54483
54484 /**
54485  * @class Roo.NestedLayoutPanel
54486  * @extends Roo.ContentPanel
54487  * @constructor
54488  * Create a new NestedLayoutPanel.
54489  * 
54490  * 
54491  * @param {Roo.BorderLayout} layout The layout for this panel
54492  * @param {String/Object} config A string to set only the title or a config object
54493  */
54494 Roo.NestedLayoutPanel = function(layout, config)
54495 {
54496     // construct with only one argument..
54497     /* FIXME - implement nicer consturctors
54498     if (layout.layout) {
54499         config = layout;
54500         layout = config.layout;
54501         delete config.layout;
54502     }
54503     if (layout.xtype && !layout.getEl) {
54504         // then layout needs constructing..
54505         layout = Roo.factory(layout, Roo);
54506     }
54507     */
54508     
54509     
54510     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54511     
54512     layout.monitorWindowResize = false; // turn off autosizing
54513     this.layout = layout;
54514     this.layout.getEl().addClass("x-layout-nested-layout");
54515     
54516     
54517     
54518     
54519 };
54520
54521 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54522
54523     setSize : function(width, height){
54524         if(!this.ignoreResize(width, height)){
54525             var size = this.adjustForComponents(width, height);
54526             var el = this.layout.getEl();
54527             el.setSize(size.width, size.height);
54528             var touch = el.dom.offsetWidth;
54529             this.layout.layout();
54530             // ie requires a double layout on the first pass
54531             if(Roo.isIE && !this.initialized){
54532                 this.initialized = true;
54533                 this.layout.layout();
54534             }
54535         }
54536     },
54537     
54538     // activate all subpanels if not currently active..
54539     
54540     setActiveState : function(active){
54541         this.active = active;
54542         if(!active){
54543             this.fireEvent("deactivate", this);
54544             return;
54545         }
54546         
54547         this.fireEvent("activate", this);
54548         // not sure if this should happen before or after..
54549         if (!this.layout) {
54550             return; // should not happen..
54551         }
54552         var reg = false;
54553         for (var r in this.layout.regions) {
54554             reg = this.layout.getRegion(r);
54555             if (reg.getActivePanel()) {
54556                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54557                 reg.setActivePanel(reg.getActivePanel());
54558                 continue;
54559             }
54560             if (!reg.panels.length) {
54561                 continue;
54562             }
54563             reg.showPanel(reg.getPanel(0));
54564         }
54565         
54566         
54567         
54568         
54569     },
54570     
54571     /**
54572      * Returns the nested BorderLayout for this panel
54573      * @return {Roo.BorderLayout} 
54574      */
54575     getLayout : function(){
54576         return this.layout;
54577     },
54578     
54579      /**
54580      * Adds a xtype elements to the layout of the nested panel
54581      * <pre><code>
54582
54583 panel.addxtype({
54584        xtype : 'ContentPanel',
54585        region: 'west',
54586        items: [ .... ]
54587    }
54588 );
54589
54590 panel.addxtype({
54591         xtype : 'NestedLayoutPanel',
54592         region: 'west',
54593         layout: {
54594            center: { },
54595            west: { }   
54596         },
54597         items : [ ... list of content panels or nested layout panels.. ]
54598    }
54599 );
54600 </code></pre>
54601      * @param {Object} cfg Xtype definition of item to add.
54602      */
54603     addxtype : function(cfg) {
54604         return this.layout.addxtype(cfg);
54605     
54606     }
54607 });
54608
54609 Roo.ScrollPanel = function(el, config, content){
54610     config = config || {};
54611     config.fitToFrame = true;
54612     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54613     
54614     this.el.dom.style.overflow = "hidden";
54615     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54616     this.el.removeClass("x-layout-inactive-content");
54617     this.el.on("mousewheel", this.onWheel, this);
54618
54619     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54620     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54621     up.unselectable(); down.unselectable();
54622     up.on("click", this.scrollUp, this);
54623     down.on("click", this.scrollDown, this);
54624     up.addClassOnOver("x-scroller-btn-over");
54625     down.addClassOnOver("x-scroller-btn-over");
54626     up.addClassOnClick("x-scroller-btn-click");
54627     down.addClassOnClick("x-scroller-btn-click");
54628     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54629
54630     this.resizeEl = this.el;
54631     this.el = wrap; this.up = up; this.down = down;
54632 };
54633
54634 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
54635     increment : 100,
54636     wheelIncrement : 5,
54637     scrollUp : function(){
54638         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
54639     },
54640
54641     scrollDown : function(){
54642         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
54643     },
54644
54645     afterScroll : function(){
54646         var el = this.resizeEl;
54647         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
54648         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54649         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54650     },
54651
54652     setSize : function(){
54653         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
54654         this.afterScroll();
54655     },
54656
54657     onWheel : function(e){
54658         var d = e.getWheelDelta();
54659         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
54660         this.afterScroll();
54661         e.stopEvent();
54662     },
54663
54664     setContent : function(content, loadScripts){
54665         this.resizeEl.update(content, loadScripts);
54666     }
54667
54668 });
54669
54670
54671
54672
54673
54674
54675
54676
54677
54678 /**
54679  * @class Roo.TreePanel
54680  * @extends Roo.ContentPanel
54681  * @constructor
54682  * Create a new TreePanel. - defaults to fit/scoll contents.
54683  * @param {String/Object} config A string to set only the panel's title, or a config object
54684  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
54685  */
54686 Roo.TreePanel = function(config){
54687     var el = config.el;
54688     var tree = config.tree;
54689     delete config.tree; 
54690     delete config.el; // hopefull!
54691     
54692     // wrapper for IE7 strict & safari scroll issue
54693     
54694     var treeEl = el.createChild();
54695     config.resizeEl = treeEl;
54696     
54697     
54698     
54699     Roo.TreePanel.superclass.constructor.call(this, el, config);
54700  
54701  
54702     this.tree = new Roo.tree.TreePanel(treeEl , tree);
54703     //console.log(tree);
54704     this.on('activate', function()
54705     {
54706         if (this.tree.rendered) {
54707             return;
54708         }
54709         //console.log('render tree');
54710         this.tree.render();
54711     });
54712     // this should not be needed.. - it's actually the 'el' that resizes?
54713     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
54714     
54715     //this.on('resize',  function (cp, w, h) {
54716     //        this.tree.innerCt.setWidth(w);
54717     //        this.tree.innerCt.setHeight(h);
54718     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
54719     //});
54720
54721         
54722     
54723 };
54724
54725 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
54726     fitToFrame : true,
54727     autoScroll : true
54728 });
54729
54730
54731
54732
54733
54734
54735
54736
54737
54738
54739
54740 /*
54741  * Based on:
54742  * Ext JS Library 1.1.1
54743  * Copyright(c) 2006-2007, Ext JS, LLC.
54744  *
54745  * Originally Released Under LGPL - original licence link has changed is not relivant.
54746  *
54747  * Fork - LGPL
54748  * <script type="text/javascript">
54749  */
54750  
54751
54752 /**
54753  * @class Roo.ReaderLayout
54754  * @extends Roo.BorderLayout
54755  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
54756  * center region containing two nested regions (a top one for a list view and one for item preview below),
54757  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
54758  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
54759  * expedites the setup of the overall layout and regions for this common application style.
54760  * Example:
54761  <pre><code>
54762 var reader = new Roo.ReaderLayout();
54763 var CP = Roo.ContentPanel;  // shortcut for adding
54764
54765 reader.beginUpdate();
54766 reader.add("north", new CP("north", "North"));
54767 reader.add("west", new CP("west", {title: "West"}));
54768 reader.add("east", new CP("east", {title: "East"}));
54769
54770 reader.regions.listView.add(new CP("listView", "List"));
54771 reader.regions.preview.add(new CP("preview", "Preview"));
54772 reader.endUpdate();
54773 </code></pre>
54774 * @constructor
54775 * Create a new ReaderLayout
54776 * @param {Object} config Configuration options
54777 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
54778 * document.body if omitted)
54779 */
54780 Roo.ReaderLayout = function(config, renderTo){
54781     var c = config || {size:{}};
54782     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
54783         north: c.north !== false ? Roo.apply({
54784             split:false,
54785             initialSize: 32,
54786             titlebar: false
54787         }, c.north) : false,
54788         west: c.west !== false ? Roo.apply({
54789             split:true,
54790             initialSize: 200,
54791             minSize: 175,
54792             maxSize: 400,
54793             titlebar: true,
54794             collapsible: true,
54795             animate: true,
54796             margins:{left:5,right:0,bottom:5,top:5},
54797             cmargins:{left:5,right:5,bottom:5,top:5}
54798         }, c.west) : false,
54799         east: c.east !== false ? Roo.apply({
54800             split:true,
54801             initialSize: 200,
54802             minSize: 175,
54803             maxSize: 400,
54804             titlebar: true,
54805             collapsible: true,
54806             animate: true,
54807             margins:{left:0,right:5,bottom:5,top:5},
54808             cmargins:{left:5,right:5,bottom:5,top:5}
54809         }, c.east) : false,
54810         center: Roo.apply({
54811             tabPosition: 'top',
54812             autoScroll:false,
54813             closeOnTab: true,
54814             titlebar:false,
54815             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
54816         }, c.center)
54817     });
54818
54819     this.el.addClass('x-reader');
54820
54821     this.beginUpdate();
54822
54823     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
54824         south: c.preview !== false ? Roo.apply({
54825             split:true,
54826             initialSize: 200,
54827             minSize: 100,
54828             autoScroll:true,
54829             collapsible:true,
54830             titlebar: true,
54831             cmargins:{top:5,left:0, right:0, bottom:0}
54832         }, c.preview) : false,
54833         center: Roo.apply({
54834             autoScroll:false,
54835             titlebar:false,
54836             minHeight:200
54837         }, c.listView)
54838     });
54839     this.add('center', new Roo.NestedLayoutPanel(inner,
54840             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
54841
54842     this.endUpdate();
54843
54844     this.regions.preview = inner.getRegion('south');
54845     this.regions.listView = inner.getRegion('center');
54846 };
54847
54848 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
54849  * Based on:
54850  * Ext JS Library 1.1.1
54851  * Copyright(c) 2006-2007, Ext JS, LLC.
54852  *
54853  * Originally Released Under LGPL - original licence link has changed is not relivant.
54854  *
54855  * Fork - LGPL
54856  * <script type="text/javascript">
54857  */
54858  
54859 /**
54860  * @class Roo.grid.Grid
54861  * @extends Roo.util.Observable
54862  * This class represents the primary interface of a component based grid control.
54863  * <br><br>Usage:<pre><code>
54864  var grid = new Roo.grid.Grid("my-container-id", {
54865      ds: myDataStore,
54866      cm: myColModel,
54867      selModel: mySelectionModel,
54868      autoSizeColumns: true,
54869      monitorWindowResize: false,
54870      trackMouseOver: true
54871  });
54872  // set any options
54873  grid.render();
54874  * </code></pre>
54875  * <b>Common Problems:</b><br/>
54876  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54877  * element will correct this<br/>
54878  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54879  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54880  * are unpredictable.<br/>
54881  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54882  * grid to calculate dimensions/offsets.<br/>
54883   * @constructor
54884  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54885  * The container MUST have some type of size defined for the grid to fill. The container will be
54886  * automatically set to position relative if it isn't already.
54887  * @param {Object} config A config object that sets properties on this grid.
54888  */
54889 Roo.grid.Grid = function(container, config){
54890         // initialize the container
54891         this.container = Roo.get(container);
54892         this.container.update("");
54893         this.container.setStyle("overflow", "hidden");
54894     this.container.addClass('x-grid-container');
54895
54896     this.id = this.container.id;
54897
54898     Roo.apply(this, config);
54899     // check and correct shorthanded configs
54900     if(this.ds){
54901         this.dataSource = this.ds;
54902         delete this.ds;
54903     }
54904     if(this.cm){
54905         this.colModel = this.cm;
54906         delete this.cm;
54907     }
54908     if(this.sm){
54909         this.selModel = this.sm;
54910         delete this.sm;
54911     }
54912
54913     if (this.selModel) {
54914         this.selModel = Roo.factory(this.selModel, Roo.grid);
54915         this.sm = this.selModel;
54916         this.sm.xmodule = this.xmodule || false;
54917     }
54918     if (typeof(this.colModel.config) == 'undefined') {
54919         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54920         this.cm = this.colModel;
54921         this.cm.xmodule = this.xmodule || false;
54922     }
54923     if (this.dataSource) {
54924         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54925         this.ds = this.dataSource;
54926         this.ds.xmodule = this.xmodule || false;
54927          
54928     }
54929     
54930     
54931     
54932     if(this.width){
54933         this.container.setWidth(this.width);
54934     }
54935
54936     if(this.height){
54937         this.container.setHeight(this.height);
54938     }
54939     /** @private */
54940         this.addEvents({
54941         // raw events
54942         /**
54943          * @event click
54944          * The raw click event for the entire grid.
54945          * @param {Roo.EventObject} e
54946          */
54947         "click" : true,
54948         /**
54949          * @event dblclick
54950          * The raw dblclick event for the entire grid.
54951          * @param {Roo.EventObject} e
54952          */
54953         "dblclick" : true,
54954         /**
54955          * @event contextmenu
54956          * The raw contextmenu event for the entire grid.
54957          * @param {Roo.EventObject} e
54958          */
54959         "contextmenu" : true,
54960         /**
54961          * @event mousedown
54962          * The raw mousedown event for the entire grid.
54963          * @param {Roo.EventObject} e
54964          */
54965         "mousedown" : true,
54966         /**
54967          * @event mouseup
54968          * The raw mouseup event for the entire grid.
54969          * @param {Roo.EventObject} e
54970          */
54971         "mouseup" : true,
54972         /**
54973          * @event mouseover
54974          * The raw mouseover event for the entire grid.
54975          * @param {Roo.EventObject} e
54976          */
54977         "mouseover" : true,
54978         /**
54979          * @event mouseout
54980          * The raw mouseout event for the entire grid.
54981          * @param {Roo.EventObject} e
54982          */
54983         "mouseout" : true,
54984         /**
54985          * @event keypress
54986          * The raw keypress event for the entire grid.
54987          * @param {Roo.EventObject} e
54988          */
54989         "keypress" : true,
54990         /**
54991          * @event keydown
54992          * The raw keydown event for the entire grid.
54993          * @param {Roo.EventObject} e
54994          */
54995         "keydown" : true,
54996
54997         // custom events
54998
54999         /**
55000          * @event cellclick
55001          * Fires when a cell is clicked
55002          * @param {Grid} this
55003          * @param {Number} rowIndex
55004          * @param {Number} columnIndex
55005          * @param {Roo.EventObject} e
55006          */
55007         "cellclick" : true,
55008         /**
55009          * @event celldblclick
55010          * Fires when a cell is double clicked
55011          * @param {Grid} this
55012          * @param {Number} rowIndex
55013          * @param {Number} columnIndex
55014          * @param {Roo.EventObject} e
55015          */
55016         "celldblclick" : true,
55017         /**
55018          * @event rowclick
55019          * Fires when a row is clicked
55020          * @param {Grid} this
55021          * @param {Number} rowIndex
55022          * @param {Roo.EventObject} e
55023          */
55024         "rowclick" : true,
55025         /**
55026          * @event rowdblclick
55027          * Fires when a row is double clicked
55028          * @param {Grid} this
55029          * @param {Number} rowIndex
55030          * @param {Roo.EventObject} e
55031          */
55032         "rowdblclick" : true,
55033         /**
55034          * @event headerclick
55035          * Fires when a header is clicked
55036          * @param {Grid} this
55037          * @param {Number} columnIndex
55038          * @param {Roo.EventObject} e
55039          */
55040         "headerclick" : true,
55041         /**
55042          * @event headerdblclick
55043          * Fires when a header cell is double clicked
55044          * @param {Grid} this
55045          * @param {Number} columnIndex
55046          * @param {Roo.EventObject} e
55047          */
55048         "headerdblclick" : true,
55049         /**
55050          * @event rowcontextmenu
55051          * Fires when a row is right clicked
55052          * @param {Grid} this
55053          * @param {Number} rowIndex
55054          * @param {Roo.EventObject} e
55055          */
55056         "rowcontextmenu" : true,
55057         /**
55058          * @event cellcontextmenu
55059          * Fires when a cell is right clicked
55060          * @param {Grid} this
55061          * @param {Number} rowIndex
55062          * @param {Number} cellIndex
55063          * @param {Roo.EventObject} e
55064          */
55065          "cellcontextmenu" : true,
55066         /**
55067          * @event headercontextmenu
55068          * Fires when a header is right clicked
55069          * @param {Grid} this
55070          * @param {Number} columnIndex
55071          * @param {Roo.EventObject} e
55072          */
55073         "headercontextmenu" : true,
55074         /**
55075          * @event bodyscroll
55076          * Fires when the body element is scrolled
55077          * @param {Number} scrollLeft
55078          * @param {Number} scrollTop
55079          */
55080         "bodyscroll" : true,
55081         /**
55082          * @event columnresize
55083          * Fires when the user resizes a column
55084          * @param {Number} columnIndex
55085          * @param {Number} newSize
55086          */
55087         "columnresize" : true,
55088         /**
55089          * @event columnmove
55090          * Fires when the user moves a column
55091          * @param {Number} oldIndex
55092          * @param {Number} newIndex
55093          */
55094         "columnmove" : true,
55095         /**
55096          * @event startdrag
55097          * Fires when row(s) start being dragged
55098          * @param {Grid} this
55099          * @param {Roo.GridDD} dd The drag drop object
55100          * @param {event} e The raw browser event
55101          */
55102         "startdrag" : true,
55103         /**
55104          * @event enddrag
55105          * Fires when a drag operation is complete
55106          * @param {Grid} this
55107          * @param {Roo.GridDD} dd The drag drop object
55108          * @param {event} e The raw browser event
55109          */
55110         "enddrag" : true,
55111         /**
55112          * @event dragdrop
55113          * Fires when dragged row(s) are dropped on a valid DD target
55114          * @param {Grid} this
55115          * @param {Roo.GridDD} dd The drag drop object
55116          * @param {String} targetId The target drag drop object
55117          * @param {event} e The raw browser event
55118          */
55119         "dragdrop" : true,
55120         /**
55121          * @event dragover
55122          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55123          * @param {Grid} this
55124          * @param {Roo.GridDD} dd The drag drop object
55125          * @param {String} targetId The target drag drop object
55126          * @param {event} e The raw browser event
55127          */
55128         "dragover" : true,
55129         /**
55130          * @event dragenter
55131          *  Fires when the dragged row(s) first cross another DD target while being dragged
55132          * @param {Grid} this
55133          * @param {Roo.GridDD} dd The drag drop object
55134          * @param {String} targetId The target drag drop object
55135          * @param {event} e The raw browser event
55136          */
55137         "dragenter" : true,
55138         /**
55139          * @event dragout
55140          * Fires when the dragged row(s) leave another DD target while being dragged
55141          * @param {Grid} this
55142          * @param {Roo.GridDD} dd The drag drop object
55143          * @param {String} targetId The target drag drop object
55144          * @param {event} e The raw browser event
55145          */
55146         "dragout" : true,
55147         /**
55148          * @event rowclass
55149          * Fires when a row is rendered, so you can change add a style to it.
55150          * @param {GridView} gridview   The grid view
55151          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55152          */
55153         'rowclass' : true,
55154
55155         /**
55156          * @event render
55157          * Fires when the grid is rendered
55158          * @param {Grid} grid
55159          */
55160         'render' : true
55161     });
55162
55163     Roo.grid.Grid.superclass.constructor.call(this);
55164 };
55165 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55166     
55167     /**
55168      * @cfg {String} ddGroup - drag drop group.
55169      */
55170
55171     /**
55172      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55173      */
55174     minColumnWidth : 25,
55175
55176     /**
55177      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55178      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55179      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55180      */
55181     autoSizeColumns : false,
55182
55183     /**
55184      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55185      */
55186     autoSizeHeaders : true,
55187
55188     /**
55189      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55190      */
55191     monitorWindowResize : true,
55192
55193     /**
55194      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55195      * rows measured to get a columns size. Default is 0 (all rows).
55196      */
55197     maxRowsToMeasure : 0,
55198
55199     /**
55200      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55201      */
55202     trackMouseOver : true,
55203
55204     /**
55205     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55206     */
55207     
55208     /**
55209     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55210     */
55211     enableDragDrop : false,
55212     
55213     /**
55214     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55215     */
55216     enableColumnMove : true,
55217     
55218     /**
55219     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55220     */
55221     enableColumnHide : true,
55222     
55223     /**
55224     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55225     */
55226     enableRowHeightSync : false,
55227     
55228     /**
55229     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55230     */
55231     stripeRows : true,
55232     
55233     /**
55234     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55235     */
55236     autoHeight : false,
55237
55238     /**
55239      * @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.
55240      */
55241     autoExpandColumn : false,
55242
55243     /**
55244     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55245     * Default is 50.
55246     */
55247     autoExpandMin : 50,
55248
55249     /**
55250     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55251     */
55252     autoExpandMax : 1000,
55253
55254     /**
55255     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55256     */
55257     view : null,
55258
55259     /**
55260     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55261     */
55262     loadMask : false,
55263     /**
55264     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55265     */
55266     dropTarget: false,
55267     
55268    
55269     
55270     // private
55271     rendered : false,
55272
55273     /**
55274     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55275     * of a fixed width. Default is false.
55276     */
55277     /**
55278     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55279     */
55280     /**
55281      * Called once after all setup has been completed and the grid is ready to be rendered.
55282      * @return {Roo.grid.Grid} this
55283      */
55284     render : function()
55285     {
55286         var c = this.container;
55287         // try to detect autoHeight/width mode
55288         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55289             this.autoHeight = true;
55290         }
55291         var view = this.getView();
55292         view.init(this);
55293
55294         c.on("click", this.onClick, this);
55295         c.on("dblclick", this.onDblClick, this);
55296         c.on("contextmenu", this.onContextMenu, this);
55297         c.on("keydown", this.onKeyDown, this);
55298         if (Roo.isTouch) {
55299             c.on("touchstart", this.onTouchStart, this);
55300         }
55301
55302         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55303
55304         this.getSelectionModel().init(this);
55305
55306         view.render();
55307
55308         if(this.loadMask){
55309             this.loadMask = new Roo.LoadMask(this.container,
55310                     Roo.apply({store:this.dataSource}, this.loadMask));
55311         }
55312         
55313         
55314         if (this.toolbar && this.toolbar.xtype) {
55315             this.toolbar.container = this.getView().getHeaderPanel(true);
55316             this.toolbar = new Roo.Toolbar(this.toolbar);
55317         }
55318         if (this.footer && this.footer.xtype) {
55319             this.footer.dataSource = this.getDataSource();
55320             this.footer.container = this.getView().getFooterPanel(true);
55321             this.footer = Roo.factory(this.footer, Roo);
55322         }
55323         if (this.dropTarget && this.dropTarget.xtype) {
55324             delete this.dropTarget.xtype;
55325             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55326         }
55327         
55328         
55329         this.rendered = true;
55330         this.fireEvent('render', this);
55331         return this;
55332     },
55333
55334         /**
55335          * Reconfigures the grid to use a different Store and Column Model.
55336          * The View will be bound to the new objects and refreshed.
55337          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55338          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55339          */
55340     reconfigure : function(dataSource, colModel){
55341         if(this.loadMask){
55342             this.loadMask.destroy();
55343             this.loadMask = new Roo.LoadMask(this.container,
55344                     Roo.apply({store:dataSource}, this.loadMask));
55345         }
55346         this.view.bind(dataSource, colModel);
55347         this.dataSource = dataSource;
55348         this.colModel = colModel;
55349         this.view.refresh(true);
55350     },
55351
55352     // private
55353     onKeyDown : function(e){
55354         this.fireEvent("keydown", e);
55355     },
55356
55357     /**
55358      * Destroy this grid.
55359      * @param {Boolean} removeEl True to remove the element
55360      */
55361     destroy : function(removeEl, keepListeners){
55362         if(this.loadMask){
55363             this.loadMask.destroy();
55364         }
55365         var c = this.container;
55366         c.removeAllListeners();
55367         this.view.destroy();
55368         this.colModel.purgeListeners();
55369         if(!keepListeners){
55370             this.purgeListeners();
55371         }
55372         c.update("");
55373         if(removeEl === true){
55374             c.remove();
55375         }
55376     },
55377
55378     // private
55379     processEvent : function(name, e){
55380         // does this fire select???
55381         //Roo.log('grid:processEvent '  + name);
55382         
55383         if (name != 'touchstart' ) {
55384             this.fireEvent(name, e);    
55385         }
55386         
55387         var t = e.getTarget();
55388         var v = this.view;
55389         var header = v.findHeaderIndex(t);
55390         if(header !== false){
55391             var ename = name == 'touchstart' ? 'click' : name;
55392              
55393             this.fireEvent("header" + ename, this, header, e);
55394         }else{
55395             var row = v.findRowIndex(t);
55396             var cell = v.findCellIndex(t);
55397             if (name == 'touchstart') {
55398                 // first touch is always a click.
55399                 // hopefull this happens after selection is updated.?
55400                 name = false;
55401                 
55402                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55403                     var cs = this.selModel.getSelectedCell();
55404                     if (row == cs[0] && cell == cs[1]){
55405                         name = 'dblclick';
55406                     }
55407                 }
55408                 if (typeof(this.selModel.getSelections) != 'undefined') {
55409                     var cs = this.selModel.getSelections();
55410                     var ds = this.dataSource;
55411                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55412                         name = 'dblclick';
55413                     }
55414                 }
55415                 if (!name) {
55416                     return;
55417                 }
55418             }
55419             
55420             
55421             if(row !== false){
55422                 this.fireEvent("row" + name, this, row, e);
55423                 if(cell !== false){
55424                     this.fireEvent("cell" + name, this, row, cell, e);
55425                 }
55426             }
55427         }
55428     },
55429
55430     // private
55431     onClick : function(e){
55432         this.processEvent("click", e);
55433     },
55434    // private
55435     onTouchStart : function(e){
55436         this.processEvent("touchstart", e);
55437     },
55438
55439     // private
55440     onContextMenu : function(e, t){
55441         this.processEvent("contextmenu", e);
55442     },
55443
55444     // private
55445     onDblClick : function(e){
55446         this.processEvent("dblclick", e);
55447     },
55448
55449     // private
55450     walkCells : function(row, col, step, fn, scope){
55451         var cm = this.colModel, clen = cm.getColumnCount();
55452         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55453         if(step < 0){
55454             if(col < 0){
55455                 row--;
55456                 first = false;
55457             }
55458             while(row >= 0){
55459                 if(!first){
55460                     col = clen-1;
55461                 }
55462                 first = false;
55463                 while(col >= 0){
55464                     if(fn.call(scope || this, row, col, cm) === true){
55465                         return [row, col];
55466                     }
55467                     col--;
55468                 }
55469                 row--;
55470             }
55471         } else {
55472             if(col >= clen){
55473                 row++;
55474                 first = false;
55475             }
55476             while(row < rlen){
55477                 if(!first){
55478                     col = 0;
55479                 }
55480                 first = false;
55481                 while(col < clen){
55482                     if(fn.call(scope || this, row, col, cm) === true){
55483                         return [row, col];
55484                     }
55485                     col++;
55486                 }
55487                 row++;
55488             }
55489         }
55490         return null;
55491     },
55492
55493     // private
55494     getSelections : function(){
55495         return this.selModel.getSelections();
55496     },
55497
55498     /**
55499      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55500      * but if manual update is required this method will initiate it.
55501      */
55502     autoSize : function(){
55503         if(this.rendered){
55504             this.view.layout();
55505             if(this.view.adjustForScroll){
55506                 this.view.adjustForScroll();
55507             }
55508         }
55509     },
55510
55511     /**
55512      * Returns the grid's underlying element.
55513      * @return {Element} The element
55514      */
55515     getGridEl : function(){
55516         return this.container;
55517     },
55518
55519     // private for compatibility, overridden by editor grid
55520     stopEditing : function(){},
55521
55522     /**
55523      * Returns the grid's SelectionModel.
55524      * @return {SelectionModel}
55525      */
55526     getSelectionModel : function(){
55527         if(!this.selModel){
55528             this.selModel = new Roo.grid.RowSelectionModel();
55529         }
55530         return this.selModel;
55531     },
55532
55533     /**
55534      * Returns the grid's DataSource.
55535      * @return {DataSource}
55536      */
55537     getDataSource : function(){
55538         return this.dataSource;
55539     },
55540
55541     /**
55542      * Returns the grid's ColumnModel.
55543      * @return {ColumnModel}
55544      */
55545     getColumnModel : function(){
55546         return this.colModel;
55547     },
55548
55549     /**
55550      * Returns the grid's GridView object.
55551      * @return {GridView}
55552      */
55553     getView : function(){
55554         if(!this.view){
55555             this.view = new Roo.grid.GridView(this.viewConfig);
55556         }
55557         return this.view;
55558     },
55559     /**
55560      * Called to get grid's drag proxy text, by default returns this.ddText.
55561      * @return {String}
55562      */
55563     getDragDropText : function(){
55564         var count = this.selModel.getCount();
55565         return String.format(this.ddText, count, count == 1 ? '' : 's');
55566     }
55567 });
55568 /**
55569  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55570  * %0 is replaced with the number of selected rows.
55571  * @type String
55572  */
55573 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
55574  * Based on:
55575  * Ext JS Library 1.1.1
55576  * Copyright(c) 2006-2007, Ext JS, LLC.
55577  *
55578  * Originally Released Under LGPL - original licence link has changed is not relivant.
55579  *
55580  * Fork - LGPL
55581  * <script type="text/javascript">
55582  */
55583  
55584 Roo.grid.AbstractGridView = function(){
55585         this.grid = null;
55586         
55587         this.events = {
55588             "beforerowremoved" : true,
55589             "beforerowsinserted" : true,
55590             "beforerefresh" : true,
55591             "rowremoved" : true,
55592             "rowsinserted" : true,
55593             "rowupdated" : true,
55594             "refresh" : true
55595         };
55596     Roo.grid.AbstractGridView.superclass.constructor.call(this);
55597 };
55598
55599 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
55600     rowClass : "x-grid-row",
55601     cellClass : "x-grid-cell",
55602     tdClass : "x-grid-td",
55603     hdClass : "x-grid-hd",
55604     splitClass : "x-grid-hd-split",
55605     
55606     init: function(grid){
55607         this.grid = grid;
55608                 var cid = this.grid.getGridEl().id;
55609         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
55610         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
55611         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
55612         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
55613         },
55614         
55615     getColumnRenderers : function(){
55616         var renderers = [];
55617         var cm = this.grid.colModel;
55618         var colCount = cm.getColumnCount();
55619         for(var i = 0; i < colCount; i++){
55620             renderers[i] = cm.getRenderer(i);
55621         }
55622         return renderers;
55623     },
55624     
55625     getColumnIds : function(){
55626         var ids = [];
55627         var cm = this.grid.colModel;
55628         var colCount = cm.getColumnCount();
55629         for(var i = 0; i < colCount; i++){
55630             ids[i] = cm.getColumnId(i);
55631         }
55632         return ids;
55633     },
55634     
55635     getDataIndexes : function(){
55636         if(!this.indexMap){
55637             this.indexMap = this.buildIndexMap();
55638         }
55639         return this.indexMap.colToData;
55640     },
55641     
55642     getColumnIndexByDataIndex : function(dataIndex){
55643         if(!this.indexMap){
55644             this.indexMap = this.buildIndexMap();
55645         }
55646         return this.indexMap.dataToCol[dataIndex];
55647     },
55648     
55649     /**
55650      * Set a css style for a column dynamically. 
55651      * @param {Number} colIndex The index of the column
55652      * @param {String} name The css property name
55653      * @param {String} value The css value
55654      */
55655     setCSSStyle : function(colIndex, name, value){
55656         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
55657         Roo.util.CSS.updateRule(selector, name, value);
55658     },
55659     
55660     generateRules : function(cm){
55661         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
55662         Roo.util.CSS.removeStyleSheet(rulesId);
55663         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55664             var cid = cm.getColumnId(i);
55665             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
55666                          this.tdSelector, cid, " {\n}\n",
55667                          this.hdSelector, cid, " {\n}\n",
55668                          this.splitSelector, cid, " {\n}\n");
55669         }
55670         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55671     }
55672 });/*
55673  * Based on:
55674  * Ext JS Library 1.1.1
55675  * Copyright(c) 2006-2007, Ext JS, LLC.
55676  *
55677  * Originally Released Under LGPL - original licence link has changed is not relivant.
55678  *
55679  * Fork - LGPL
55680  * <script type="text/javascript">
55681  */
55682
55683 // private
55684 // This is a support class used internally by the Grid components
55685 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
55686     this.grid = grid;
55687     this.view = grid.getView();
55688     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55689     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
55690     if(hd2){
55691         this.setHandleElId(Roo.id(hd));
55692         this.setOuterHandleElId(Roo.id(hd2));
55693     }
55694     this.scroll = false;
55695 };
55696 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
55697     maxDragWidth: 120,
55698     getDragData : function(e){
55699         var t = Roo.lib.Event.getTarget(e);
55700         var h = this.view.findHeaderCell(t);
55701         if(h){
55702             return {ddel: h.firstChild, header:h};
55703         }
55704         return false;
55705     },
55706
55707     onInitDrag : function(e){
55708         this.view.headersDisabled = true;
55709         var clone = this.dragData.ddel.cloneNode(true);
55710         clone.id = Roo.id();
55711         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
55712         this.proxy.update(clone);
55713         return true;
55714     },
55715
55716     afterValidDrop : function(){
55717         var v = this.view;
55718         setTimeout(function(){
55719             v.headersDisabled = false;
55720         }, 50);
55721     },
55722
55723     afterInvalidDrop : function(){
55724         var v = this.view;
55725         setTimeout(function(){
55726             v.headersDisabled = false;
55727         }, 50);
55728     }
55729 });
55730 /*
55731  * Based on:
55732  * Ext JS Library 1.1.1
55733  * Copyright(c) 2006-2007, Ext JS, LLC.
55734  *
55735  * Originally Released Under LGPL - original licence link has changed is not relivant.
55736  *
55737  * Fork - LGPL
55738  * <script type="text/javascript">
55739  */
55740 // private
55741 // This is a support class used internally by the Grid components
55742 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
55743     this.grid = grid;
55744     this.view = grid.getView();
55745     // split the proxies so they don't interfere with mouse events
55746     this.proxyTop = Roo.DomHelper.append(document.body, {
55747         cls:"col-move-top", html:"&#160;"
55748     }, true);
55749     this.proxyBottom = Roo.DomHelper.append(document.body, {
55750         cls:"col-move-bottom", html:"&#160;"
55751     }, true);
55752     this.proxyTop.hide = this.proxyBottom.hide = function(){
55753         this.setLeftTop(-100,-100);
55754         this.setStyle("visibility", "hidden");
55755     };
55756     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55757     // temporarily disabled
55758     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
55759     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
55760 };
55761 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
55762     proxyOffsets : [-4, -9],
55763     fly: Roo.Element.fly,
55764
55765     getTargetFromEvent : function(e){
55766         var t = Roo.lib.Event.getTarget(e);
55767         var cindex = this.view.findCellIndex(t);
55768         if(cindex !== false){
55769             return this.view.getHeaderCell(cindex);
55770         }
55771         return null;
55772     },
55773
55774     nextVisible : function(h){
55775         var v = this.view, cm = this.grid.colModel;
55776         h = h.nextSibling;
55777         while(h){
55778             if(!cm.isHidden(v.getCellIndex(h))){
55779                 return h;
55780             }
55781             h = h.nextSibling;
55782         }
55783         return null;
55784     },
55785
55786     prevVisible : function(h){
55787         var v = this.view, cm = this.grid.colModel;
55788         h = h.prevSibling;
55789         while(h){
55790             if(!cm.isHidden(v.getCellIndex(h))){
55791                 return h;
55792             }
55793             h = h.prevSibling;
55794         }
55795         return null;
55796     },
55797
55798     positionIndicator : function(h, n, e){
55799         var x = Roo.lib.Event.getPageX(e);
55800         var r = Roo.lib.Dom.getRegion(n.firstChild);
55801         var px, pt, py = r.top + this.proxyOffsets[1];
55802         if((r.right - x) <= (r.right-r.left)/2){
55803             px = r.right+this.view.borderWidth;
55804             pt = "after";
55805         }else{
55806             px = r.left;
55807             pt = "before";
55808         }
55809         var oldIndex = this.view.getCellIndex(h);
55810         var newIndex = this.view.getCellIndex(n);
55811
55812         if(this.grid.colModel.isFixed(newIndex)){
55813             return false;
55814         }
55815
55816         var locked = this.grid.colModel.isLocked(newIndex);
55817
55818         if(pt == "after"){
55819             newIndex++;
55820         }
55821         if(oldIndex < newIndex){
55822             newIndex--;
55823         }
55824         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
55825             return false;
55826         }
55827         px +=  this.proxyOffsets[0];
55828         this.proxyTop.setLeftTop(px, py);
55829         this.proxyTop.show();
55830         if(!this.bottomOffset){
55831             this.bottomOffset = this.view.mainHd.getHeight();
55832         }
55833         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
55834         this.proxyBottom.show();
55835         return pt;
55836     },
55837
55838     onNodeEnter : function(n, dd, e, data){
55839         if(data.header != n){
55840             this.positionIndicator(data.header, n, e);
55841         }
55842     },
55843
55844     onNodeOver : function(n, dd, e, data){
55845         var result = false;
55846         if(data.header != n){
55847             result = this.positionIndicator(data.header, n, e);
55848         }
55849         if(!result){
55850             this.proxyTop.hide();
55851             this.proxyBottom.hide();
55852         }
55853         return result ? this.dropAllowed : this.dropNotAllowed;
55854     },
55855
55856     onNodeOut : function(n, dd, e, data){
55857         this.proxyTop.hide();
55858         this.proxyBottom.hide();
55859     },
55860
55861     onNodeDrop : function(n, dd, e, data){
55862         var h = data.header;
55863         if(h != n){
55864             var cm = this.grid.colModel;
55865             var x = Roo.lib.Event.getPageX(e);
55866             var r = Roo.lib.Dom.getRegion(n.firstChild);
55867             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
55868             var oldIndex = this.view.getCellIndex(h);
55869             var newIndex = this.view.getCellIndex(n);
55870             var locked = cm.isLocked(newIndex);
55871             if(pt == "after"){
55872                 newIndex++;
55873             }
55874             if(oldIndex < newIndex){
55875                 newIndex--;
55876             }
55877             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55878                 return false;
55879             }
55880             cm.setLocked(oldIndex, locked, true);
55881             cm.moveColumn(oldIndex, newIndex);
55882             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55883             return true;
55884         }
55885         return false;
55886     }
55887 });
55888 /*
55889  * Based on:
55890  * Ext JS Library 1.1.1
55891  * Copyright(c) 2006-2007, Ext JS, LLC.
55892  *
55893  * Originally Released Under LGPL - original licence link has changed is not relivant.
55894  *
55895  * Fork - LGPL
55896  * <script type="text/javascript">
55897  */
55898   
55899 /**
55900  * @class Roo.grid.GridView
55901  * @extends Roo.util.Observable
55902  *
55903  * @constructor
55904  * @param {Object} config
55905  */
55906 Roo.grid.GridView = function(config){
55907     Roo.grid.GridView.superclass.constructor.call(this);
55908     this.el = null;
55909
55910     Roo.apply(this, config);
55911 };
55912
55913 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55914
55915     unselectable :  'unselectable="on"',
55916     unselectableCls :  'x-unselectable',
55917     
55918     
55919     rowClass : "x-grid-row",
55920
55921     cellClass : "x-grid-col",
55922
55923     tdClass : "x-grid-td",
55924
55925     hdClass : "x-grid-hd",
55926
55927     splitClass : "x-grid-split",
55928
55929     sortClasses : ["sort-asc", "sort-desc"],
55930
55931     enableMoveAnim : false,
55932
55933     hlColor: "C3DAF9",
55934
55935     dh : Roo.DomHelper,
55936
55937     fly : Roo.Element.fly,
55938
55939     css : Roo.util.CSS,
55940
55941     borderWidth: 1,
55942
55943     splitOffset: 3,
55944
55945     scrollIncrement : 22,
55946
55947     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55948
55949     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55950
55951     bind : function(ds, cm){
55952         if(this.ds){
55953             this.ds.un("load", this.onLoad, this);
55954             this.ds.un("datachanged", this.onDataChange, this);
55955             this.ds.un("add", this.onAdd, this);
55956             this.ds.un("remove", this.onRemove, this);
55957             this.ds.un("update", this.onUpdate, this);
55958             this.ds.un("clear", this.onClear, this);
55959         }
55960         if(ds){
55961             ds.on("load", this.onLoad, this);
55962             ds.on("datachanged", this.onDataChange, this);
55963             ds.on("add", this.onAdd, this);
55964             ds.on("remove", this.onRemove, this);
55965             ds.on("update", this.onUpdate, this);
55966             ds.on("clear", this.onClear, this);
55967         }
55968         this.ds = ds;
55969
55970         if(this.cm){
55971             this.cm.un("widthchange", this.onColWidthChange, this);
55972             this.cm.un("headerchange", this.onHeaderChange, this);
55973             this.cm.un("hiddenchange", this.onHiddenChange, this);
55974             this.cm.un("columnmoved", this.onColumnMove, this);
55975             this.cm.un("columnlockchange", this.onColumnLock, this);
55976         }
55977         if(cm){
55978             this.generateRules(cm);
55979             cm.on("widthchange", this.onColWidthChange, this);
55980             cm.on("headerchange", this.onHeaderChange, this);
55981             cm.on("hiddenchange", this.onHiddenChange, this);
55982             cm.on("columnmoved", this.onColumnMove, this);
55983             cm.on("columnlockchange", this.onColumnLock, this);
55984         }
55985         this.cm = cm;
55986     },
55987
55988     init: function(grid){
55989         Roo.grid.GridView.superclass.init.call(this, grid);
55990
55991         this.bind(grid.dataSource, grid.colModel);
55992
55993         grid.on("headerclick", this.handleHeaderClick, this);
55994
55995         if(grid.trackMouseOver){
55996             grid.on("mouseover", this.onRowOver, this);
55997             grid.on("mouseout", this.onRowOut, this);
55998         }
55999         grid.cancelTextSelection = function(){};
56000         this.gridId = grid.id;
56001
56002         var tpls = this.templates || {};
56003
56004         if(!tpls.master){
56005             tpls.master = new Roo.Template(
56006                '<div class="x-grid" hidefocus="true">',
56007                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56008                   '<div class="x-grid-topbar"></div>',
56009                   '<div class="x-grid-scroller"><div></div></div>',
56010                   '<div class="x-grid-locked">',
56011                       '<div class="x-grid-header">{lockedHeader}</div>',
56012                       '<div class="x-grid-body">{lockedBody}</div>',
56013                   "</div>",
56014                   '<div class="x-grid-viewport">',
56015                       '<div class="x-grid-header">{header}</div>',
56016                       '<div class="x-grid-body">{body}</div>',
56017                   "</div>",
56018                   '<div class="x-grid-bottombar"></div>',
56019                  
56020                   '<div class="x-grid-resize-proxy">&#160;</div>',
56021                "</div>"
56022             );
56023             tpls.master.disableformats = true;
56024         }
56025
56026         if(!tpls.header){
56027             tpls.header = new Roo.Template(
56028                '<table border="0" cellspacing="0" cellpadding="0">',
56029                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56030                "</table>{splits}"
56031             );
56032             tpls.header.disableformats = true;
56033         }
56034         tpls.header.compile();
56035
56036         if(!tpls.hcell){
56037             tpls.hcell = new Roo.Template(
56038                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56039                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56040                 "</div></td>"
56041              );
56042              tpls.hcell.disableFormats = true;
56043         }
56044         tpls.hcell.compile();
56045
56046         if(!tpls.hsplit){
56047             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56048                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56049             tpls.hsplit.disableFormats = true;
56050         }
56051         tpls.hsplit.compile();
56052
56053         if(!tpls.body){
56054             tpls.body = new Roo.Template(
56055                '<table border="0" cellspacing="0" cellpadding="0">',
56056                "<tbody>{rows}</tbody>",
56057                "</table>"
56058             );
56059             tpls.body.disableFormats = true;
56060         }
56061         tpls.body.compile();
56062
56063         if(!tpls.row){
56064             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56065             tpls.row.disableFormats = true;
56066         }
56067         tpls.row.compile();
56068
56069         if(!tpls.cell){
56070             tpls.cell = new Roo.Template(
56071                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56072                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56073                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56074                 "</td>"
56075             );
56076             tpls.cell.disableFormats = true;
56077         }
56078         tpls.cell.compile();
56079
56080         this.templates = tpls;
56081     },
56082
56083     // remap these for backwards compat
56084     onColWidthChange : function(){
56085         this.updateColumns.apply(this, arguments);
56086     },
56087     onHeaderChange : function(){
56088         this.updateHeaders.apply(this, arguments);
56089     }, 
56090     onHiddenChange : function(){
56091         this.handleHiddenChange.apply(this, arguments);
56092     },
56093     onColumnMove : function(){
56094         this.handleColumnMove.apply(this, arguments);
56095     },
56096     onColumnLock : function(){
56097         this.handleLockChange.apply(this, arguments);
56098     },
56099
56100     onDataChange : function(){
56101         this.refresh();
56102         this.updateHeaderSortState();
56103     },
56104
56105     onClear : function(){
56106         this.refresh();
56107     },
56108
56109     onUpdate : function(ds, record){
56110         this.refreshRow(record);
56111     },
56112
56113     refreshRow : function(record){
56114         var ds = this.ds, index;
56115         if(typeof record == 'number'){
56116             index = record;
56117             record = ds.getAt(index);
56118         }else{
56119             index = ds.indexOf(record);
56120         }
56121         this.insertRows(ds, index, index, true);
56122         this.onRemove(ds, record, index+1, true);
56123         this.syncRowHeights(index, index);
56124         this.layout();
56125         this.fireEvent("rowupdated", this, index, record);
56126     },
56127
56128     onAdd : function(ds, records, index){
56129         this.insertRows(ds, index, index + (records.length-1));
56130     },
56131
56132     onRemove : function(ds, record, index, isUpdate){
56133         if(isUpdate !== true){
56134             this.fireEvent("beforerowremoved", this, index, record);
56135         }
56136         var bt = this.getBodyTable(), lt = this.getLockedTable();
56137         if(bt.rows[index]){
56138             bt.firstChild.removeChild(bt.rows[index]);
56139         }
56140         if(lt.rows[index]){
56141             lt.firstChild.removeChild(lt.rows[index]);
56142         }
56143         if(isUpdate !== true){
56144             this.stripeRows(index);
56145             this.syncRowHeights(index, index);
56146             this.layout();
56147             this.fireEvent("rowremoved", this, index, record);
56148         }
56149     },
56150
56151     onLoad : function(){
56152         this.scrollToTop();
56153     },
56154
56155     /**
56156      * Scrolls the grid to the top
56157      */
56158     scrollToTop : function(){
56159         if(this.scroller){
56160             this.scroller.dom.scrollTop = 0;
56161             this.syncScroll();
56162         }
56163     },
56164
56165     /**
56166      * Gets a panel in the header of the grid that can be used for toolbars etc.
56167      * After modifying the contents of this panel a call to grid.autoSize() may be
56168      * required to register any changes in size.
56169      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56170      * @return Roo.Element
56171      */
56172     getHeaderPanel : function(doShow){
56173         if(doShow){
56174             this.headerPanel.show();
56175         }
56176         return this.headerPanel;
56177     },
56178
56179     /**
56180      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56181      * After modifying the contents of this panel a call to grid.autoSize() may be
56182      * required to register any changes in size.
56183      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56184      * @return Roo.Element
56185      */
56186     getFooterPanel : function(doShow){
56187         if(doShow){
56188             this.footerPanel.show();
56189         }
56190         return this.footerPanel;
56191     },
56192
56193     initElements : function(){
56194         var E = Roo.Element;
56195         var el = this.grid.getGridEl().dom.firstChild;
56196         var cs = el.childNodes;
56197
56198         this.el = new E(el);
56199         
56200          this.focusEl = new E(el.firstChild);
56201         this.focusEl.swallowEvent("click", true);
56202         
56203         this.headerPanel = new E(cs[1]);
56204         this.headerPanel.enableDisplayMode("block");
56205
56206         this.scroller = new E(cs[2]);
56207         this.scrollSizer = new E(this.scroller.dom.firstChild);
56208
56209         this.lockedWrap = new E(cs[3]);
56210         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56211         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56212
56213         this.mainWrap = new E(cs[4]);
56214         this.mainHd = new E(this.mainWrap.dom.firstChild);
56215         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56216
56217         this.footerPanel = new E(cs[5]);
56218         this.footerPanel.enableDisplayMode("block");
56219
56220         this.resizeProxy = new E(cs[6]);
56221
56222         this.headerSelector = String.format(
56223            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56224            this.lockedHd.id, this.mainHd.id
56225         );
56226
56227         this.splitterSelector = String.format(
56228            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56229            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56230         );
56231     },
56232     idToCssName : function(s)
56233     {
56234         return s.replace(/[^a-z0-9]+/ig, '-');
56235     },
56236
56237     getHeaderCell : function(index){
56238         return Roo.DomQuery.select(this.headerSelector)[index];
56239     },
56240
56241     getHeaderCellMeasure : function(index){
56242         return this.getHeaderCell(index).firstChild;
56243     },
56244
56245     getHeaderCellText : function(index){
56246         return this.getHeaderCell(index).firstChild.firstChild;
56247     },
56248
56249     getLockedTable : function(){
56250         return this.lockedBody.dom.firstChild;
56251     },
56252
56253     getBodyTable : function(){
56254         return this.mainBody.dom.firstChild;
56255     },
56256
56257     getLockedRow : function(index){
56258         return this.getLockedTable().rows[index];
56259     },
56260
56261     getRow : function(index){
56262         return this.getBodyTable().rows[index];
56263     },
56264
56265     getRowComposite : function(index){
56266         if(!this.rowEl){
56267             this.rowEl = new Roo.CompositeElementLite();
56268         }
56269         var els = [], lrow, mrow;
56270         if(lrow = this.getLockedRow(index)){
56271             els.push(lrow);
56272         }
56273         if(mrow = this.getRow(index)){
56274             els.push(mrow);
56275         }
56276         this.rowEl.elements = els;
56277         return this.rowEl;
56278     },
56279     /**
56280      * Gets the 'td' of the cell
56281      * 
56282      * @param {Integer} rowIndex row to select
56283      * @param {Integer} colIndex column to select
56284      * 
56285      * @return {Object} 
56286      */
56287     getCell : function(rowIndex, colIndex){
56288         var locked = this.cm.getLockedCount();
56289         var source;
56290         if(colIndex < locked){
56291             source = this.lockedBody.dom.firstChild;
56292         }else{
56293             source = this.mainBody.dom.firstChild;
56294             colIndex -= locked;
56295         }
56296         return source.rows[rowIndex].childNodes[colIndex];
56297     },
56298
56299     getCellText : function(rowIndex, colIndex){
56300         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56301     },
56302
56303     getCellBox : function(cell){
56304         var b = this.fly(cell).getBox();
56305         if(Roo.isOpera){ // opera fails to report the Y
56306             b.y = cell.offsetTop + this.mainBody.getY();
56307         }
56308         return b;
56309     },
56310
56311     getCellIndex : function(cell){
56312         var id = String(cell.className).match(this.cellRE);
56313         if(id){
56314             return parseInt(id[1], 10);
56315         }
56316         return 0;
56317     },
56318
56319     findHeaderIndex : function(n){
56320         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56321         return r ? this.getCellIndex(r) : false;
56322     },
56323
56324     findHeaderCell : function(n){
56325         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56326         return r ? r : false;
56327     },
56328
56329     findRowIndex : function(n){
56330         if(!n){
56331             return false;
56332         }
56333         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56334         return r ? r.rowIndex : false;
56335     },
56336
56337     findCellIndex : function(node){
56338         var stop = this.el.dom;
56339         while(node && node != stop){
56340             if(this.findRE.test(node.className)){
56341                 return this.getCellIndex(node);
56342             }
56343             node = node.parentNode;
56344         }
56345         return false;
56346     },
56347
56348     getColumnId : function(index){
56349         return this.cm.getColumnId(index);
56350     },
56351
56352     getSplitters : function()
56353     {
56354         if(this.splitterSelector){
56355            return Roo.DomQuery.select(this.splitterSelector);
56356         }else{
56357             return null;
56358       }
56359     },
56360
56361     getSplitter : function(index){
56362         return this.getSplitters()[index];
56363     },
56364
56365     onRowOver : function(e, t){
56366         var row;
56367         if((row = this.findRowIndex(t)) !== false){
56368             this.getRowComposite(row).addClass("x-grid-row-over");
56369         }
56370     },
56371
56372     onRowOut : function(e, t){
56373         var row;
56374         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56375             this.getRowComposite(row).removeClass("x-grid-row-over");
56376         }
56377     },
56378
56379     renderHeaders : function(){
56380         var cm = this.cm;
56381         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56382         var cb = [], lb = [], sb = [], lsb = [], p = {};
56383         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56384             p.cellId = "x-grid-hd-0-" + i;
56385             p.splitId = "x-grid-csplit-0-" + i;
56386             p.id = cm.getColumnId(i);
56387             p.value = cm.getColumnHeader(i) || "";
56388             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56389             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56390             if(!cm.isLocked(i)){
56391                 cb[cb.length] = ct.apply(p);
56392                 sb[sb.length] = st.apply(p);
56393             }else{
56394                 lb[lb.length] = ct.apply(p);
56395                 lsb[lsb.length] = st.apply(p);
56396             }
56397         }
56398         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56399                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56400     },
56401
56402     updateHeaders : function(){
56403         var html = this.renderHeaders();
56404         this.lockedHd.update(html[0]);
56405         this.mainHd.update(html[1]);
56406     },
56407
56408     /**
56409      * Focuses the specified row.
56410      * @param {Number} row The row index
56411      */
56412     focusRow : function(row)
56413     {
56414         //Roo.log('GridView.focusRow');
56415         var x = this.scroller.dom.scrollLeft;
56416         this.focusCell(row, 0, false);
56417         this.scroller.dom.scrollLeft = x;
56418     },
56419
56420     /**
56421      * Focuses the specified cell.
56422      * @param {Number} row The row index
56423      * @param {Number} col The column index
56424      * @param {Boolean} hscroll false to disable horizontal scrolling
56425      */
56426     focusCell : function(row, col, hscroll)
56427     {
56428         //Roo.log('GridView.focusCell');
56429         var el = this.ensureVisible(row, col, hscroll);
56430         this.focusEl.alignTo(el, "tl-tl");
56431         if(Roo.isGecko){
56432             this.focusEl.focus();
56433         }else{
56434             this.focusEl.focus.defer(1, this.focusEl);
56435         }
56436     },
56437
56438     /**
56439      * Scrolls the specified cell into view
56440      * @param {Number} row The row index
56441      * @param {Number} col The column index
56442      * @param {Boolean} hscroll false to disable horizontal scrolling
56443      */
56444     ensureVisible : function(row, col, hscroll)
56445     {
56446         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56447         //return null; //disable for testing.
56448         if(typeof row != "number"){
56449             row = row.rowIndex;
56450         }
56451         if(row < 0 && row >= this.ds.getCount()){
56452             return  null;
56453         }
56454         col = (col !== undefined ? col : 0);
56455         var cm = this.grid.colModel;
56456         while(cm.isHidden(col)){
56457             col++;
56458         }
56459
56460         var el = this.getCell(row, col);
56461         if(!el){
56462             return null;
56463         }
56464         var c = this.scroller.dom;
56465
56466         var ctop = parseInt(el.offsetTop, 10);
56467         var cleft = parseInt(el.offsetLeft, 10);
56468         var cbot = ctop + el.offsetHeight;
56469         var cright = cleft + el.offsetWidth;
56470         
56471         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56472         var stop = parseInt(c.scrollTop, 10);
56473         var sleft = parseInt(c.scrollLeft, 10);
56474         var sbot = stop + ch;
56475         var sright = sleft + c.clientWidth;
56476         /*
56477         Roo.log('GridView.ensureVisible:' +
56478                 ' ctop:' + ctop +
56479                 ' c.clientHeight:' + c.clientHeight +
56480                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56481                 ' stop:' + stop +
56482                 ' cbot:' + cbot +
56483                 ' sbot:' + sbot +
56484                 ' ch:' + ch  
56485                 );
56486         */
56487         if(ctop < stop){
56488              c.scrollTop = ctop;
56489             //Roo.log("set scrolltop to ctop DISABLE?");
56490         }else if(cbot > sbot){
56491             //Roo.log("set scrolltop to cbot-ch");
56492             c.scrollTop = cbot-ch;
56493         }
56494         
56495         if(hscroll !== false){
56496             if(cleft < sleft){
56497                 c.scrollLeft = cleft;
56498             }else if(cright > sright){
56499                 c.scrollLeft = cright-c.clientWidth;
56500             }
56501         }
56502          
56503         return el;
56504     },
56505
56506     updateColumns : function(){
56507         this.grid.stopEditing();
56508         var cm = this.grid.colModel, colIds = this.getColumnIds();
56509         //var totalWidth = cm.getTotalWidth();
56510         var pos = 0;
56511         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56512             //if(cm.isHidden(i)) continue;
56513             var w = cm.getColumnWidth(i);
56514             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56515             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56516         }
56517         this.updateSplitters();
56518     },
56519
56520     generateRules : function(cm){
56521         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56522         Roo.util.CSS.removeStyleSheet(rulesId);
56523         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56524             var cid = cm.getColumnId(i);
56525             var align = '';
56526             if(cm.config[i].align){
56527                 align = 'text-align:'+cm.config[i].align+';';
56528             }
56529             var hidden = '';
56530             if(cm.isHidden(i)){
56531                 hidden = 'display:none;';
56532             }
56533             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56534             ruleBuf.push(
56535                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56536                     this.hdSelector, cid, " {\n", align, width, "}\n",
56537                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56538                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56539         }
56540         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56541     },
56542
56543     updateSplitters : function(){
56544         var cm = this.cm, s = this.getSplitters();
56545         if(s){ // splitters not created yet
56546             var pos = 0, locked = true;
56547             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56548                 if(cm.isHidden(i)) {
56549                     continue;
56550                 }
56551                 var w = cm.getColumnWidth(i); // make sure it's a number
56552                 if(!cm.isLocked(i) && locked){
56553                     pos = 0;
56554                     locked = false;
56555                 }
56556                 pos += w;
56557                 s[i].style.left = (pos-this.splitOffset) + "px";
56558             }
56559         }
56560     },
56561
56562     handleHiddenChange : function(colModel, colIndex, hidden){
56563         if(hidden){
56564             this.hideColumn(colIndex);
56565         }else{
56566             this.unhideColumn(colIndex);
56567         }
56568     },
56569
56570     hideColumn : function(colIndex){
56571         var cid = this.getColumnId(colIndex);
56572         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56573         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56574         if(Roo.isSafari){
56575             this.updateHeaders();
56576         }
56577         this.updateSplitters();
56578         this.layout();
56579     },
56580
56581     unhideColumn : function(colIndex){
56582         var cid = this.getColumnId(colIndex);
56583         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
56584         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
56585
56586         if(Roo.isSafari){
56587             this.updateHeaders();
56588         }
56589         this.updateSplitters();
56590         this.layout();
56591     },
56592
56593     insertRows : function(dm, firstRow, lastRow, isUpdate){
56594         if(firstRow == 0 && lastRow == dm.getCount()-1){
56595             this.refresh();
56596         }else{
56597             if(!isUpdate){
56598                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
56599             }
56600             var s = this.getScrollState();
56601             var markup = this.renderRows(firstRow, lastRow);
56602             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
56603             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
56604             this.restoreScroll(s);
56605             if(!isUpdate){
56606                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
56607                 this.syncRowHeights(firstRow, lastRow);
56608                 this.stripeRows(firstRow);
56609                 this.layout();
56610             }
56611         }
56612     },
56613
56614     bufferRows : function(markup, target, index){
56615         var before = null, trows = target.rows, tbody = target.tBodies[0];
56616         if(index < trows.length){
56617             before = trows[index];
56618         }
56619         var b = document.createElement("div");
56620         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
56621         var rows = b.firstChild.rows;
56622         for(var i = 0, len = rows.length; i < len; i++){
56623             if(before){
56624                 tbody.insertBefore(rows[0], before);
56625             }else{
56626                 tbody.appendChild(rows[0]);
56627             }
56628         }
56629         b.innerHTML = "";
56630         b = null;
56631     },
56632
56633     deleteRows : function(dm, firstRow, lastRow){
56634         if(dm.getRowCount()<1){
56635             this.fireEvent("beforerefresh", this);
56636             this.mainBody.update("");
56637             this.lockedBody.update("");
56638             this.fireEvent("refresh", this);
56639         }else{
56640             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
56641             var bt = this.getBodyTable();
56642             var tbody = bt.firstChild;
56643             var rows = bt.rows;
56644             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
56645                 tbody.removeChild(rows[firstRow]);
56646             }
56647             this.stripeRows(firstRow);
56648             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
56649         }
56650     },
56651
56652     updateRows : function(dataSource, firstRow, lastRow){
56653         var s = this.getScrollState();
56654         this.refresh();
56655         this.restoreScroll(s);
56656     },
56657
56658     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
56659         if(!noRefresh){
56660            this.refresh();
56661         }
56662         this.updateHeaderSortState();
56663     },
56664
56665     getScrollState : function(){
56666         
56667         var sb = this.scroller.dom;
56668         return {left: sb.scrollLeft, top: sb.scrollTop};
56669     },
56670
56671     stripeRows : function(startRow){
56672         if(!this.grid.stripeRows || this.ds.getCount() < 1){
56673             return;
56674         }
56675         startRow = startRow || 0;
56676         var rows = this.getBodyTable().rows;
56677         var lrows = this.getLockedTable().rows;
56678         var cls = ' x-grid-row-alt ';
56679         for(var i = startRow, len = rows.length; i < len; i++){
56680             var row = rows[i], lrow = lrows[i];
56681             var isAlt = ((i+1) % 2 == 0);
56682             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
56683             if(isAlt == hasAlt){
56684                 continue;
56685             }
56686             if(isAlt){
56687                 row.className += " x-grid-row-alt";
56688             }else{
56689                 row.className = row.className.replace("x-grid-row-alt", "");
56690             }
56691             if(lrow){
56692                 lrow.className = row.className;
56693             }
56694         }
56695     },
56696
56697     restoreScroll : function(state){
56698         //Roo.log('GridView.restoreScroll');
56699         var sb = this.scroller.dom;
56700         sb.scrollLeft = state.left;
56701         sb.scrollTop = state.top;
56702         this.syncScroll();
56703     },
56704
56705     syncScroll : function(){
56706         //Roo.log('GridView.syncScroll');
56707         var sb = this.scroller.dom;
56708         var sh = this.mainHd.dom;
56709         var bs = this.mainBody.dom;
56710         var lv = this.lockedBody.dom;
56711         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
56712         lv.scrollTop = bs.scrollTop = sb.scrollTop;
56713     },
56714
56715     handleScroll : function(e){
56716         this.syncScroll();
56717         var sb = this.scroller.dom;
56718         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
56719         e.stopEvent();
56720     },
56721
56722     handleWheel : function(e){
56723         var d = e.getWheelDelta();
56724         this.scroller.dom.scrollTop -= d*22;
56725         // set this here to prevent jumpy scrolling on large tables
56726         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
56727         e.stopEvent();
56728     },
56729
56730     renderRows : function(startRow, endRow){
56731         // pull in all the crap needed to render rows
56732         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
56733         var colCount = cm.getColumnCount();
56734
56735         if(ds.getCount() < 1){
56736             return ["", ""];
56737         }
56738
56739         // build a map for all the columns
56740         var cs = [];
56741         for(var i = 0; i < colCount; i++){
56742             var name = cm.getDataIndex(i);
56743             cs[i] = {
56744                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
56745                 renderer : cm.getRenderer(i),
56746                 id : cm.getColumnId(i),
56747                 locked : cm.isLocked(i),
56748                 has_editor : cm.isCellEditable(i)
56749             };
56750         }
56751
56752         startRow = startRow || 0;
56753         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
56754
56755         // records to render
56756         var rs = ds.getRange(startRow, endRow);
56757
56758         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
56759     },
56760
56761     // As much as I hate to duplicate code, this was branched because FireFox really hates
56762     // [].join("") on strings. The performance difference was substantial enough to
56763     // branch this function
56764     doRender : Roo.isGecko ?
56765             function(cs, rs, ds, startRow, colCount, stripe){
56766                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56767                 // buffers
56768                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56769                 
56770                 var hasListener = this.grid.hasListener('rowclass');
56771                 var rowcfg = {};
56772                 for(var j = 0, len = rs.length; j < len; j++){
56773                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
56774                     for(var i = 0; i < colCount; i++){
56775                         c = cs[i];
56776                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56777                         p.id = c.id;
56778                         p.css = p.attr = "";
56779                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56780                         if(p.value == undefined || p.value === "") {
56781                             p.value = "&#160;";
56782                         }
56783                         if(c.has_editor){
56784                             p.css += ' x-grid-editable-cell';
56785                         }
56786                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
56787                             p.css +=  ' x-grid-dirty-cell';
56788                         }
56789                         var markup = ct.apply(p);
56790                         if(!c.locked){
56791                             cb+= markup;
56792                         }else{
56793                             lcb+= markup;
56794                         }
56795                     }
56796                     var alt = [];
56797                     if(stripe && ((rowIndex+1) % 2 == 0)){
56798                         alt.push("x-grid-row-alt")
56799                     }
56800                     if(r.dirty){
56801                         alt.push(  " x-grid-dirty-row");
56802                     }
56803                     rp.cells = lcb;
56804                     if(this.getRowClass){
56805                         alt.push(this.getRowClass(r, rowIndex));
56806                     }
56807                     if (hasListener) {
56808                         rowcfg = {
56809                              
56810                             record: r,
56811                             rowIndex : rowIndex,
56812                             rowClass : ''
56813                         };
56814                         this.grid.fireEvent('rowclass', this, rowcfg);
56815                         alt.push(rowcfg.rowClass);
56816                     }
56817                     rp.alt = alt.join(" ");
56818                     lbuf+= rt.apply(rp);
56819                     rp.cells = cb;
56820                     buf+=  rt.apply(rp);
56821                 }
56822                 return [lbuf, buf];
56823             } :
56824             function(cs, rs, ds, startRow, colCount, stripe){
56825                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56826                 // buffers
56827                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56828                 var hasListener = this.grid.hasListener('rowclass');
56829  
56830                 var rowcfg = {};
56831                 for(var j = 0, len = rs.length; j < len; j++){
56832                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
56833                     for(var i = 0; i < colCount; i++){
56834                         c = cs[i];
56835                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56836                         p.id = c.id;
56837                         p.css = p.attr = "";
56838                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56839                         if(p.value == undefined || p.value === "") {
56840                             p.value = "&#160;";
56841                         }
56842                         //Roo.log(c);
56843                          if(c.has_editor){
56844                             p.css += ' x-grid-editable-cell';
56845                         }
56846                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
56847                             p.css += ' x-grid-dirty-cell' 
56848                         }
56849                         
56850                         var markup = ct.apply(p);
56851                         if(!c.locked){
56852                             cb[cb.length] = markup;
56853                         }else{
56854                             lcb[lcb.length] = markup;
56855                         }
56856                     }
56857                     var alt = [];
56858                     if(stripe && ((rowIndex+1) % 2 == 0)){
56859                         alt.push( "x-grid-row-alt");
56860                     }
56861                     if(r.dirty){
56862                         alt.push(" x-grid-dirty-row");
56863                     }
56864                     rp.cells = lcb;
56865                     if(this.getRowClass){
56866                         alt.push( this.getRowClass(r, rowIndex));
56867                     }
56868                     if (hasListener) {
56869                         rowcfg = {
56870                              
56871                             record: r,
56872                             rowIndex : rowIndex,
56873                             rowClass : ''
56874                         };
56875                         this.grid.fireEvent('rowclass', this, rowcfg);
56876                         alt.push(rowcfg.rowClass);
56877                     }
56878                     
56879                     rp.alt = alt.join(" ");
56880                     rp.cells = lcb.join("");
56881                     lbuf[lbuf.length] = rt.apply(rp);
56882                     rp.cells = cb.join("");
56883                     buf[buf.length] =  rt.apply(rp);
56884                 }
56885                 return [lbuf.join(""), buf.join("")];
56886             },
56887
56888     renderBody : function(){
56889         var markup = this.renderRows();
56890         var bt = this.templates.body;
56891         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56892     },
56893
56894     /**
56895      * Refreshes the grid
56896      * @param {Boolean} headersToo
56897      */
56898     refresh : function(headersToo){
56899         this.fireEvent("beforerefresh", this);
56900         this.grid.stopEditing();
56901         var result = this.renderBody();
56902         this.lockedBody.update(result[0]);
56903         this.mainBody.update(result[1]);
56904         if(headersToo === true){
56905             this.updateHeaders();
56906             this.updateColumns();
56907             this.updateSplitters();
56908             this.updateHeaderSortState();
56909         }
56910         this.syncRowHeights();
56911         this.layout();
56912         this.fireEvent("refresh", this);
56913     },
56914
56915     handleColumnMove : function(cm, oldIndex, newIndex){
56916         this.indexMap = null;
56917         var s = this.getScrollState();
56918         this.refresh(true);
56919         this.restoreScroll(s);
56920         this.afterMove(newIndex);
56921     },
56922
56923     afterMove : function(colIndex){
56924         if(this.enableMoveAnim && Roo.enableFx){
56925             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56926         }
56927         // if multisort - fix sortOrder, and reload..
56928         if (this.grid.dataSource.multiSort) {
56929             // the we can call sort again..
56930             var dm = this.grid.dataSource;
56931             var cm = this.grid.colModel;
56932             var so = [];
56933             for(var i = 0; i < cm.config.length; i++ ) {
56934                 
56935                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56936                     continue; // dont' bother, it's not in sort list or being set.
56937                 }
56938                 
56939                 so.push(cm.config[i].dataIndex);
56940             };
56941             dm.sortOrder = so;
56942             dm.load(dm.lastOptions);
56943             
56944             
56945         }
56946         
56947     },
56948
56949     updateCell : function(dm, rowIndex, dataIndex){
56950         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56951         if(typeof colIndex == "undefined"){ // not present in grid
56952             return;
56953         }
56954         var cm = this.grid.colModel;
56955         var cell = this.getCell(rowIndex, colIndex);
56956         var cellText = this.getCellText(rowIndex, colIndex);
56957
56958         var p = {
56959             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56960             id : cm.getColumnId(colIndex),
56961             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56962         };
56963         var renderer = cm.getRenderer(colIndex);
56964         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56965         if(typeof val == "undefined" || val === "") {
56966             val = "&#160;";
56967         }
56968         cellText.innerHTML = val;
56969         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56970         this.syncRowHeights(rowIndex, rowIndex);
56971     },
56972
56973     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56974         var maxWidth = 0;
56975         if(this.grid.autoSizeHeaders){
56976             var h = this.getHeaderCellMeasure(colIndex);
56977             maxWidth = Math.max(maxWidth, h.scrollWidth);
56978         }
56979         var tb, index;
56980         if(this.cm.isLocked(colIndex)){
56981             tb = this.getLockedTable();
56982             index = colIndex;
56983         }else{
56984             tb = this.getBodyTable();
56985             index = colIndex - this.cm.getLockedCount();
56986         }
56987         if(tb && tb.rows){
56988             var rows = tb.rows;
56989             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56990             for(var i = 0; i < stopIndex; i++){
56991                 var cell = rows[i].childNodes[index].firstChild;
56992                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56993             }
56994         }
56995         return maxWidth + /*margin for error in IE*/ 5;
56996     },
56997     /**
56998      * Autofit a column to its content.
56999      * @param {Number} colIndex
57000      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57001      */
57002      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57003          if(this.cm.isHidden(colIndex)){
57004              return; // can't calc a hidden column
57005          }
57006         if(forceMinSize){
57007             var cid = this.cm.getColumnId(colIndex);
57008             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57009            if(this.grid.autoSizeHeaders){
57010                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57011            }
57012         }
57013         var newWidth = this.calcColumnWidth(colIndex);
57014         this.cm.setColumnWidth(colIndex,
57015             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57016         if(!suppressEvent){
57017             this.grid.fireEvent("columnresize", colIndex, newWidth);
57018         }
57019     },
57020
57021     /**
57022      * Autofits all columns to their content and then expands to fit any extra space in the grid
57023      */
57024      autoSizeColumns : function(){
57025         var cm = this.grid.colModel;
57026         var colCount = cm.getColumnCount();
57027         for(var i = 0; i < colCount; i++){
57028             this.autoSizeColumn(i, true, true);
57029         }
57030         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57031             this.fitColumns();
57032         }else{
57033             this.updateColumns();
57034             this.layout();
57035         }
57036     },
57037
57038     /**
57039      * Autofits all columns to the grid's width proportionate with their current size
57040      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57041      */
57042     fitColumns : function(reserveScrollSpace){
57043         var cm = this.grid.colModel;
57044         var colCount = cm.getColumnCount();
57045         var cols = [];
57046         var width = 0;
57047         var i, w;
57048         for (i = 0; i < colCount; i++){
57049             if(!cm.isHidden(i) && !cm.isFixed(i)){
57050                 w = cm.getColumnWidth(i);
57051                 cols.push(i);
57052                 cols.push(w);
57053                 width += w;
57054             }
57055         }
57056         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57057         if(reserveScrollSpace){
57058             avail -= 17;
57059         }
57060         var frac = (avail - cm.getTotalWidth())/width;
57061         while (cols.length){
57062             w = cols.pop();
57063             i = cols.pop();
57064             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57065         }
57066         this.updateColumns();
57067         this.layout();
57068     },
57069
57070     onRowSelect : function(rowIndex){
57071         var row = this.getRowComposite(rowIndex);
57072         row.addClass("x-grid-row-selected");
57073     },
57074
57075     onRowDeselect : function(rowIndex){
57076         var row = this.getRowComposite(rowIndex);
57077         row.removeClass("x-grid-row-selected");
57078     },
57079
57080     onCellSelect : function(row, col){
57081         var cell = this.getCell(row, col);
57082         if(cell){
57083             Roo.fly(cell).addClass("x-grid-cell-selected");
57084         }
57085     },
57086
57087     onCellDeselect : function(row, col){
57088         var cell = this.getCell(row, col);
57089         if(cell){
57090             Roo.fly(cell).removeClass("x-grid-cell-selected");
57091         }
57092     },
57093
57094     updateHeaderSortState : function(){
57095         
57096         // sort state can be single { field: xxx, direction : yyy}
57097         // or   { xxx=>ASC , yyy : DESC ..... }
57098         
57099         var mstate = {};
57100         if (!this.ds.multiSort) { 
57101             var state = this.ds.getSortState();
57102             if(!state){
57103                 return;
57104             }
57105             mstate[state.field] = state.direction;
57106             // FIXME... - this is not used here.. but might be elsewhere..
57107             this.sortState = state;
57108             
57109         } else {
57110             mstate = this.ds.sortToggle;
57111         }
57112         //remove existing sort classes..
57113         
57114         var sc = this.sortClasses;
57115         var hds = this.el.select(this.headerSelector).removeClass(sc);
57116         
57117         for(var f in mstate) {
57118         
57119             var sortColumn = this.cm.findColumnIndex(f);
57120             
57121             if(sortColumn != -1){
57122                 var sortDir = mstate[f];        
57123                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57124             }
57125         }
57126         
57127          
57128         
57129     },
57130
57131
57132     handleHeaderClick : function(g, index,e){
57133         
57134         Roo.log("header click");
57135         
57136         if (Roo.isTouch) {
57137             // touch events on header are handled by context
57138             this.handleHdCtx(g,index,e);
57139             return;
57140         }
57141         
57142         
57143         if(this.headersDisabled){
57144             return;
57145         }
57146         var dm = g.dataSource, cm = g.colModel;
57147         if(!cm.isSortable(index)){
57148             return;
57149         }
57150         g.stopEditing();
57151         
57152         if (dm.multiSort) {
57153             // update the sortOrder
57154             var so = [];
57155             for(var i = 0; i < cm.config.length; i++ ) {
57156                 
57157                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57158                     continue; // dont' bother, it's not in sort list or being set.
57159                 }
57160                 
57161                 so.push(cm.config[i].dataIndex);
57162             };
57163             dm.sortOrder = so;
57164         }
57165         
57166         
57167         dm.sort(cm.getDataIndex(index));
57168     },
57169
57170
57171     destroy : function(){
57172         if(this.colMenu){
57173             this.colMenu.removeAll();
57174             Roo.menu.MenuMgr.unregister(this.colMenu);
57175             this.colMenu.getEl().remove();
57176             delete this.colMenu;
57177         }
57178         if(this.hmenu){
57179             this.hmenu.removeAll();
57180             Roo.menu.MenuMgr.unregister(this.hmenu);
57181             this.hmenu.getEl().remove();
57182             delete this.hmenu;
57183         }
57184         if(this.grid.enableColumnMove){
57185             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57186             if(dds){
57187                 for(var dd in dds){
57188                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57189                         var elid = dds[dd].dragElId;
57190                         dds[dd].unreg();
57191                         Roo.get(elid).remove();
57192                     } else if(dds[dd].config.isTarget){
57193                         dds[dd].proxyTop.remove();
57194                         dds[dd].proxyBottom.remove();
57195                         dds[dd].unreg();
57196                     }
57197                     if(Roo.dd.DDM.locationCache[dd]){
57198                         delete Roo.dd.DDM.locationCache[dd];
57199                     }
57200                 }
57201                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57202             }
57203         }
57204         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57205         this.bind(null, null);
57206         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57207     },
57208
57209     handleLockChange : function(){
57210         this.refresh(true);
57211     },
57212
57213     onDenyColumnLock : function(){
57214
57215     },
57216
57217     onDenyColumnHide : function(){
57218
57219     },
57220
57221     handleHdMenuClick : function(item){
57222         var index = this.hdCtxIndex;
57223         var cm = this.cm, ds = this.ds;
57224         switch(item.id){
57225             case "asc":
57226                 ds.sort(cm.getDataIndex(index), "ASC");
57227                 break;
57228             case "desc":
57229                 ds.sort(cm.getDataIndex(index), "DESC");
57230                 break;
57231             case "lock":
57232                 var lc = cm.getLockedCount();
57233                 if(cm.getColumnCount(true) <= lc+1){
57234                     this.onDenyColumnLock();
57235                     return;
57236                 }
57237                 if(lc != index){
57238                     cm.setLocked(index, true, true);
57239                     cm.moveColumn(index, lc);
57240                     this.grid.fireEvent("columnmove", index, lc);
57241                 }else{
57242                     cm.setLocked(index, true);
57243                 }
57244             break;
57245             case "unlock":
57246                 var lc = cm.getLockedCount();
57247                 if((lc-1) != index){
57248                     cm.setLocked(index, false, true);
57249                     cm.moveColumn(index, lc-1);
57250                     this.grid.fireEvent("columnmove", index, lc-1);
57251                 }else{
57252                     cm.setLocked(index, false);
57253                 }
57254             break;
57255             case 'wider': // used to expand cols on touch..
57256             case 'narrow':
57257                 var cw = cm.getColumnWidth(index);
57258                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57259                 cw = Math.max(0, cw);
57260                 cw = Math.min(cw,4000);
57261                 cm.setColumnWidth(index, cw);
57262                 break;
57263                 
57264             default:
57265                 index = cm.getIndexById(item.id.substr(4));
57266                 if(index != -1){
57267                     if(item.checked && cm.getColumnCount(true) <= 1){
57268                         this.onDenyColumnHide();
57269                         return false;
57270                     }
57271                     cm.setHidden(index, item.checked);
57272                 }
57273         }
57274         return true;
57275     },
57276
57277     beforeColMenuShow : function(){
57278         var cm = this.cm,  colCount = cm.getColumnCount();
57279         this.colMenu.removeAll();
57280         for(var i = 0; i < colCount; i++){
57281             this.colMenu.add(new Roo.menu.CheckItem({
57282                 id: "col-"+cm.getColumnId(i),
57283                 text: cm.getColumnHeader(i),
57284                 checked: !cm.isHidden(i),
57285                 hideOnClick:false
57286             }));
57287         }
57288     },
57289
57290     handleHdCtx : function(g, index, e){
57291         e.stopEvent();
57292         var hd = this.getHeaderCell(index);
57293         this.hdCtxIndex = index;
57294         var ms = this.hmenu.items, cm = this.cm;
57295         ms.get("asc").setDisabled(!cm.isSortable(index));
57296         ms.get("desc").setDisabled(!cm.isSortable(index));
57297         if(this.grid.enableColLock !== false){
57298             ms.get("lock").setDisabled(cm.isLocked(index));
57299             ms.get("unlock").setDisabled(!cm.isLocked(index));
57300         }
57301         this.hmenu.show(hd, "tl-bl");
57302     },
57303
57304     handleHdOver : function(e){
57305         var hd = this.findHeaderCell(e.getTarget());
57306         if(hd && !this.headersDisabled){
57307             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57308                this.fly(hd).addClass("x-grid-hd-over");
57309             }
57310         }
57311     },
57312
57313     handleHdOut : function(e){
57314         var hd = this.findHeaderCell(e.getTarget());
57315         if(hd){
57316             this.fly(hd).removeClass("x-grid-hd-over");
57317         }
57318     },
57319
57320     handleSplitDblClick : function(e, t){
57321         var i = this.getCellIndex(t);
57322         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57323             this.autoSizeColumn(i, true);
57324             this.layout();
57325         }
57326     },
57327
57328     render : function(){
57329
57330         var cm = this.cm;
57331         var colCount = cm.getColumnCount();
57332
57333         if(this.grid.monitorWindowResize === true){
57334             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57335         }
57336         var header = this.renderHeaders();
57337         var body = this.templates.body.apply({rows:""});
57338         var html = this.templates.master.apply({
57339             lockedBody: body,
57340             body: body,
57341             lockedHeader: header[0],
57342             header: header[1]
57343         });
57344
57345         //this.updateColumns();
57346
57347         this.grid.getGridEl().dom.innerHTML = html;
57348
57349         this.initElements();
57350         
57351         // a kludge to fix the random scolling effect in webkit
57352         this.el.on("scroll", function() {
57353             this.el.dom.scrollTop=0; // hopefully not recursive..
57354         },this);
57355
57356         this.scroller.on("scroll", this.handleScroll, this);
57357         this.lockedBody.on("mousewheel", this.handleWheel, this);
57358         this.mainBody.on("mousewheel", this.handleWheel, this);
57359
57360         this.mainHd.on("mouseover", this.handleHdOver, this);
57361         this.mainHd.on("mouseout", this.handleHdOut, this);
57362         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57363                 {delegate: "."+this.splitClass});
57364
57365         this.lockedHd.on("mouseover", this.handleHdOver, this);
57366         this.lockedHd.on("mouseout", this.handleHdOut, this);
57367         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57368                 {delegate: "."+this.splitClass});
57369
57370         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57371             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57372         }
57373
57374         this.updateSplitters();
57375
57376         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57377             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57378             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57379         }
57380
57381         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57382             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57383             this.hmenu.add(
57384                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57385                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57386             );
57387             if(this.grid.enableColLock !== false){
57388                 this.hmenu.add('-',
57389                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57390                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57391                 );
57392             }
57393             if (Roo.isTouch) {
57394                  this.hmenu.add('-',
57395                     {id:"wider", text: this.columnsWiderText},
57396                     {id:"narrow", text: this.columnsNarrowText }
57397                 );
57398                 
57399                  
57400             }
57401             
57402             if(this.grid.enableColumnHide !== false){
57403
57404                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57405                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57406                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57407
57408                 this.hmenu.add('-',
57409                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57410                 );
57411             }
57412             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57413
57414             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57415         }
57416
57417         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57418             this.dd = new Roo.grid.GridDragZone(this.grid, {
57419                 ddGroup : this.grid.ddGroup || 'GridDD'
57420             });
57421             
57422         }
57423
57424         /*
57425         for(var i = 0; i < colCount; i++){
57426             if(cm.isHidden(i)){
57427                 this.hideColumn(i);
57428             }
57429             if(cm.config[i].align){
57430                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57431                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57432             }
57433         }*/
57434         
57435         this.updateHeaderSortState();
57436
57437         this.beforeInitialResize();
57438         this.layout(true);
57439
57440         // two part rendering gives faster view to the user
57441         this.renderPhase2.defer(1, this);
57442     },
57443
57444     renderPhase2 : function(){
57445         // render the rows now
57446         this.refresh();
57447         if(this.grid.autoSizeColumns){
57448             this.autoSizeColumns();
57449         }
57450     },
57451
57452     beforeInitialResize : function(){
57453
57454     },
57455
57456     onColumnSplitterMoved : function(i, w){
57457         this.userResized = true;
57458         var cm = this.grid.colModel;
57459         cm.setColumnWidth(i, w, true);
57460         var cid = cm.getColumnId(i);
57461         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57462         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57463         this.updateSplitters();
57464         this.layout();
57465         this.grid.fireEvent("columnresize", i, w);
57466     },
57467
57468     syncRowHeights : function(startIndex, endIndex){
57469         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57470             startIndex = startIndex || 0;
57471             var mrows = this.getBodyTable().rows;
57472             var lrows = this.getLockedTable().rows;
57473             var len = mrows.length-1;
57474             endIndex = Math.min(endIndex || len, len);
57475             for(var i = startIndex; i <= endIndex; i++){
57476                 var m = mrows[i], l = lrows[i];
57477                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57478                 m.style.height = l.style.height = h + "px";
57479             }
57480         }
57481     },
57482
57483     layout : function(initialRender, is2ndPass){
57484         var g = this.grid;
57485         var auto = g.autoHeight;
57486         var scrollOffset = 16;
57487         var c = g.getGridEl(), cm = this.cm,
57488                 expandCol = g.autoExpandColumn,
57489                 gv = this;
57490         //c.beginMeasure();
57491
57492         if(!c.dom.offsetWidth){ // display:none?
57493             if(initialRender){
57494                 this.lockedWrap.show();
57495                 this.mainWrap.show();
57496             }
57497             return;
57498         }
57499
57500         var hasLock = this.cm.isLocked(0);
57501
57502         var tbh = this.headerPanel.getHeight();
57503         var bbh = this.footerPanel.getHeight();
57504
57505         if(auto){
57506             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57507             var newHeight = ch + c.getBorderWidth("tb");
57508             if(g.maxHeight){
57509                 newHeight = Math.min(g.maxHeight, newHeight);
57510             }
57511             c.setHeight(newHeight);
57512         }
57513
57514         if(g.autoWidth){
57515             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57516         }
57517
57518         var s = this.scroller;
57519
57520         var csize = c.getSize(true);
57521
57522         this.el.setSize(csize.width, csize.height);
57523
57524         this.headerPanel.setWidth(csize.width);
57525         this.footerPanel.setWidth(csize.width);
57526
57527         var hdHeight = this.mainHd.getHeight();
57528         var vw = csize.width;
57529         var vh = csize.height - (tbh + bbh);
57530
57531         s.setSize(vw, vh);
57532
57533         var bt = this.getBodyTable();
57534         
57535         if(cm.getLockedCount() == cm.config.length){
57536             bt = this.getLockedTable();
57537         }
57538         
57539         var ltWidth = hasLock ?
57540                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57541
57542         var scrollHeight = bt.offsetHeight;
57543         var scrollWidth = ltWidth + bt.offsetWidth;
57544         var vscroll = false, hscroll = false;
57545
57546         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57547
57548         var lw = this.lockedWrap, mw = this.mainWrap;
57549         var lb = this.lockedBody, mb = this.mainBody;
57550
57551         setTimeout(function(){
57552             var t = s.dom.offsetTop;
57553             var w = s.dom.clientWidth,
57554                 h = s.dom.clientHeight;
57555
57556             lw.setTop(t);
57557             lw.setSize(ltWidth, h);
57558
57559             mw.setLeftTop(ltWidth, t);
57560             mw.setSize(w-ltWidth, h);
57561
57562             lb.setHeight(h-hdHeight);
57563             mb.setHeight(h-hdHeight);
57564
57565             if(is2ndPass !== true && !gv.userResized && expandCol){
57566                 // high speed resize without full column calculation
57567                 
57568                 var ci = cm.getIndexById(expandCol);
57569                 if (ci < 0) {
57570                     ci = cm.findColumnIndex(expandCol);
57571                 }
57572                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57573                 var expandId = cm.getColumnId(ci);
57574                 var  tw = cm.getTotalWidth(false);
57575                 var currentWidth = cm.getColumnWidth(ci);
57576                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57577                 if(currentWidth != cw){
57578                     cm.setColumnWidth(ci, cw, true);
57579                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57580                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57581                     gv.updateSplitters();
57582                     gv.layout(false, true);
57583                 }
57584             }
57585
57586             if(initialRender){
57587                 lw.show();
57588                 mw.show();
57589             }
57590             //c.endMeasure();
57591         }, 10);
57592     },
57593
57594     onWindowResize : function(){
57595         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
57596             return;
57597         }
57598         this.layout();
57599     },
57600
57601     appendFooter : function(parentEl){
57602         return null;
57603     },
57604
57605     sortAscText : "Sort Ascending",
57606     sortDescText : "Sort Descending",
57607     lockText : "Lock Column",
57608     unlockText : "Unlock Column",
57609     columnsText : "Columns",
57610  
57611     columnsWiderText : "Wider",
57612     columnsNarrowText : "Thinner"
57613 });
57614
57615
57616 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
57617     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
57618     this.proxy.el.addClass('x-grid3-col-dd');
57619 };
57620
57621 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
57622     handleMouseDown : function(e){
57623
57624     },
57625
57626     callHandleMouseDown : function(e){
57627         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
57628     }
57629 });
57630 /*
57631  * Based on:
57632  * Ext JS Library 1.1.1
57633  * Copyright(c) 2006-2007, Ext JS, LLC.
57634  *
57635  * Originally Released Under LGPL - original licence link has changed is not relivant.
57636  *
57637  * Fork - LGPL
57638  * <script type="text/javascript">
57639  */
57640  
57641 // private
57642 // This is a support class used internally by the Grid components
57643 Roo.grid.SplitDragZone = function(grid, hd, hd2){
57644     this.grid = grid;
57645     this.view = grid.getView();
57646     this.proxy = this.view.resizeProxy;
57647     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
57648         "gridSplitters" + this.grid.getGridEl().id, {
57649         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
57650     });
57651     this.setHandleElId(Roo.id(hd));
57652     this.setOuterHandleElId(Roo.id(hd2));
57653     this.scroll = false;
57654 };
57655 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
57656     fly: Roo.Element.fly,
57657
57658     b4StartDrag : function(x, y){
57659         this.view.headersDisabled = true;
57660         this.proxy.setHeight(this.view.mainWrap.getHeight());
57661         var w = this.cm.getColumnWidth(this.cellIndex);
57662         var minw = Math.max(w-this.grid.minColumnWidth, 0);
57663         this.resetConstraints();
57664         this.setXConstraint(minw, 1000);
57665         this.setYConstraint(0, 0);
57666         this.minX = x - minw;
57667         this.maxX = x + 1000;
57668         this.startPos = x;
57669         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
57670     },
57671
57672
57673     handleMouseDown : function(e){
57674         ev = Roo.EventObject.setEvent(e);
57675         var t = this.fly(ev.getTarget());
57676         if(t.hasClass("x-grid-split")){
57677             this.cellIndex = this.view.getCellIndex(t.dom);
57678             this.split = t.dom;
57679             this.cm = this.grid.colModel;
57680             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
57681                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
57682             }
57683         }
57684     },
57685
57686     endDrag : function(e){
57687         this.view.headersDisabled = false;
57688         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
57689         var diff = endX - this.startPos;
57690         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
57691     },
57692
57693     autoOffset : function(){
57694         this.setDelta(0,0);
57695     }
57696 });/*
57697  * Based on:
57698  * Ext JS Library 1.1.1
57699  * Copyright(c) 2006-2007, Ext JS, LLC.
57700  *
57701  * Originally Released Under LGPL - original licence link has changed is not relivant.
57702  *
57703  * Fork - LGPL
57704  * <script type="text/javascript">
57705  */
57706  
57707 // private
57708 // This is a support class used internally by the Grid components
57709 Roo.grid.GridDragZone = function(grid, config){
57710     this.view = grid.getView();
57711     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
57712     if(this.view.lockedBody){
57713         this.setHandleElId(Roo.id(this.view.mainBody.dom));
57714         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
57715     }
57716     this.scroll = false;
57717     this.grid = grid;
57718     this.ddel = document.createElement('div');
57719     this.ddel.className = 'x-grid-dd-wrap';
57720 };
57721
57722 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
57723     ddGroup : "GridDD",
57724
57725     getDragData : function(e){
57726         var t = Roo.lib.Event.getTarget(e);
57727         var rowIndex = this.view.findRowIndex(t);
57728         var sm = this.grid.selModel;
57729             
57730         //Roo.log(rowIndex);
57731         
57732         if (sm.getSelectedCell) {
57733             // cell selection..
57734             if (!sm.getSelectedCell()) {
57735                 return false;
57736             }
57737             if (rowIndex != sm.getSelectedCell()[0]) {
57738                 return false;
57739             }
57740         
57741         }
57742         
57743         if(rowIndex !== false){
57744             
57745             // if editorgrid.. 
57746             
57747             
57748             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
57749                
57750             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
57751               //  
57752             //}
57753             if (e.hasModifier()){
57754                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
57755             }
57756             
57757             Roo.log("getDragData");
57758             
57759             return {
57760                 grid: this.grid,
57761                 ddel: this.ddel,
57762                 rowIndex: rowIndex,
57763                 selections:sm.getSelections ? sm.getSelections() : (
57764                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
57765                 )
57766             };
57767         }
57768         return false;
57769     },
57770
57771     onInitDrag : function(e){
57772         var data = this.dragData;
57773         this.ddel.innerHTML = this.grid.getDragDropText();
57774         this.proxy.update(this.ddel);
57775         // fire start drag?
57776     },
57777
57778     afterRepair : function(){
57779         this.dragging = false;
57780     },
57781
57782     getRepairXY : function(e, data){
57783         return false;
57784     },
57785
57786     onEndDrag : function(data, e){
57787         // fire end drag?
57788     },
57789
57790     onValidDrop : function(dd, e, id){
57791         // fire drag drop?
57792         this.hideProxy();
57793     },
57794
57795     beforeInvalidDrop : function(e, id){
57796
57797     }
57798 });/*
57799  * Based on:
57800  * Ext JS Library 1.1.1
57801  * Copyright(c) 2006-2007, Ext JS, LLC.
57802  *
57803  * Originally Released Under LGPL - original licence link has changed is not relivant.
57804  *
57805  * Fork - LGPL
57806  * <script type="text/javascript">
57807  */
57808  
57809
57810 /**
57811  * @class Roo.grid.ColumnModel
57812  * @extends Roo.util.Observable
57813  * This is the default implementation of a ColumnModel used by the Grid. It defines
57814  * the columns in the grid.
57815  * <br>Usage:<br>
57816  <pre><code>
57817  var colModel = new Roo.grid.ColumnModel([
57818         {header: "Ticker", width: 60, sortable: true, locked: true},
57819         {header: "Company Name", width: 150, sortable: true},
57820         {header: "Market Cap.", width: 100, sortable: true},
57821         {header: "$ Sales", width: 100, sortable: true, renderer: money},
57822         {header: "Employees", width: 100, sortable: true, resizable: false}
57823  ]);
57824  </code></pre>
57825  * <p>
57826  
57827  * The config options listed for this class are options which may appear in each
57828  * individual column definition.
57829  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
57830  * @constructor
57831  * @param {Object} config An Array of column config objects. See this class's
57832  * config objects for details.
57833 */
57834 Roo.grid.ColumnModel = function(config){
57835         /**
57836      * The config passed into the constructor
57837      */
57838     this.config = config;
57839     this.lookup = {};
57840
57841     // if no id, create one
57842     // if the column does not have a dataIndex mapping,
57843     // map it to the order it is in the config
57844     for(var i = 0, len = config.length; i < len; i++){
57845         var c = config[i];
57846         if(typeof c.dataIndex == "undefined"){
57847             c.dataIndex = i;
57848         }
57849         if(typeof c.renderer == "string"){
57850             c.renderer = Roo.util.Format[c.renderer];
57851         }
57852         if(typeof c.id == "undefined"){
57853             c.id = Roo.id();
57854         }
57855         if(c.editor && c.editor.xtype){
57856             c.editor  = Roo.factory(c.editor, Roo.grid);
57857         }
57858         if(c.editor && c.editor.isFormField){
57859             c.editor = new Roo.grid.GridEditor(c.editor);
57860         }
57861         this.lookup[c.id] = c;
57862     }
57863
57864     /**
57865      * The width of columns which have no width specified (defaults to 100)
57866      * @type Number
57867      */
57868     this.defaultWidth = 100;
57869
57870     /**
57871      * Default sortable of columns which have no sortable specified (defaults to false)
57872      * @type Boolean
57873      */
57874     this.defaultSortable = false;
57875
57876     this.addEvents({
57877         /**
57878              * @event widthchange
57879              * Fires when the width of a column changes.
57880              * @param {ColumnModel} this
57881              * @param {Number} columnIndex The column index
57882              * @param {Number} newWidth The new width
57883              */
57884             "widthchange": true,
57885         /**
57886              * @event headerchange
57887              * Fires when the text of a header changes.
57888              * @param {ColumnModel} this
57889              * @param {Number} columnIndex The column index
57890              * @param {Number} newText The new header text
57891              */
57892             "headerchange": true,
57893         /**
57894              * @event hiddenchange
57895              * Fires when a column is hidden or "unhidden".
57896              * @param {ColumnModel} this
57897              * @param {Number} columnIndex The column index
57898              * @param {Boolean} hidden true if hidden, false otherwise
57899              */
57900             "hiddenchange": true,
57901             /**
57902          * @event columnmoved
57903          * Fires when a column is moved.
57904          * @param {ColumnModel} this
57905          * @param {Number} oldIndex
57906          * @param {Number} newIndex
57907          */
57908         "columnmoved" : true,
57909         /**
57910          * @event columlockchange
57911          * Fires when a column's locked state is changed
57912          * @param {ColumnModel} this
57913          * @param {Number} colIndex
57914          * @param {Boolean} locked true if locked
57915          */
57916         "columnlockchange" : true
57917     });
57918     Roo.grid.ColumnModel.superclass.constructor.call(this);
57919 };
57920 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57921     /**
57922      * @cfg {String} header The header text to display in the Grid view.
57923      */
57924     /**
57925      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57926      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57927      * specified, the column's index is used as an index into the Record's data Array.
57928      */
57929     /**
57930      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57931      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57932      */
57933     /**
57934      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57935      * Defaults to the value of the {@link #defaultSortable} property.
57936      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57937      */
57938     /**
57939      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57940      */
57941     /**
57942      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57943      */
57944     /**
57945      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57946      */
57947     /**
57948      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57949      */
57950     /**
57951      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57952      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57953      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57954      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57955      */
57956        /**
57957      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57958      */
57959     /**
57960      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57961      */
57962     /**
57963      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
57964      */
57965     /**
57966      * @cfg {String} cursor (Optional)
57967      */
57968     /**
57969      * @cfg {String} tooltip (Optional)
57970      */
57971     /**
57972      * @cfg {Number} xs (Optional)
57973      */
57974     /**
57975      * @cfg {Number} sm (Optional)
57976      */
57977     /**
57978      * @cfg {Number} md (Optional)
57979      */
57980     /**
57981      * @cfg {Number} lg (Optional)
57982      */
57983     /**
57984      * Returns the id of the column at the specified index.
57985      * @param {Number} index The column index
57986      * @return {String} the id
57987      */
57988     getColumnId : function(index){
57989         return this.config[index].id;
57990     },
57991
57992     /**
57993      * Returns the column for a specified id.
57994      * @param {String} id The column id
57995      * @return {Object} the column
57996      */
57997     getColumnById : function(id){
57998         return this.lookup[id];
57999     },
58000
58001     
58002     /**
58003      * Returns the column for a specified dataIndex.
58004      * @param {String} dataIndex The column dataIndex
58005      * @return {Object|Boolean} the column or false if not found
58006      */
58007     getColumnByDataIndex: function(dataIndex){
58008         var index = this.findColumnIndex(dataIndex);
58009         return index > -1 ? this.config[index] : false;
58010     },
58011     
58012     /**
58013      * Returns the index for a specified column id.
58014      * @param {String} id The column id
58015      * @return {Number} the index, or -1 if not found
58016      */
58017     getIndexById : function(id){
58018         for(var i = 0, len = this.config.length; i < len; i++){
58019             if(this.config[i].id == id){
58020                 return i;
58021             }
58022         }
58023         return -1;
58024     },
58025     
58026     /**
58027      * Returns the index for a specified column dataIndex.
58028      * @param {String} dataIndex The column dataIndex
58029      * @return {Number} the index, or -1 if not found
58030      */
58031     
58032     findColumnIndex : function(dataIndex){
58033         for(var i = 0, len = this.config.length; i < len; i++){
58034             if(this.config[i].dataIndex == dataIndex){
58035                 return i;
58036             }
58037         }
58038         return -1;
58039     },
58040     
58041     
58042     moveColumn : function(oldIndex, newIndex){
58043         var c = this.config[oldIndex];
58044         this.config.splice(oldIndex, 1);
58045         this.config.splice(newIndex, 0, c);
58046         this.dataMap = null;
58047         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58048     },
58049
58050     isLocked : function(colIndex){
58051         return this.config[colIndex].locked === true;
58052     },
58053
58054     setLocked : function(colIndex, value, suppressEvent){
58055         if(this.isLocked(colIndex) == value){
58056             return;
58057         }
58058         this.config[colIndex].locked = value;
58059         if(!suppressEvent){
58060             this.fireEvent("columnlockchange", this, colIndex, value);
58061         }
58062     },
58063
58064     getTotalLockedWidth : function(){
58065         var totalWidth = 0;
58066         for(var i = 0; i < this.config.length; i++){
58067             if(this.isLocked(i) && !this.isHidden(i)){
58068                 this.totalWidth += this.getColumnWidth(i);
58069             }
58070         }
58071         return totalWidth;
58072     },
58073
58074     getLockedCount : function(){
58075         for(var i = 0, len = this.config.length; i < len; i++){
58076             if(!this.isLocked(i)){
58077                 return i;
58078             }
58079         }
58080         
58081         return this.config.length;
58082     },
58083
58084     /**
58085      * Returns the number of columns.
58086      * @return {Number}
58087      */
58088     getColumnCount : function(visibleOnly){
58089         if(visibleOnly === true){
58090             var c = 0;
58091             for(var i = 0, len = this.config.length; i < len; i++){
58092                 if(!this.isHidden(i)){
58093                     c++;
58094                 }
58095             }
58096             return c;
58097         }
58098         return this.config.length;
58099     },
58100
58101     /**
58102      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58103      * @param {Function} fn
58104      * @param {Object} scope (optional)
58105      * @return {Array} result
58106      */
58107     getColumnsBy : function(fn, scope){
58108         var r = [];
58109         for(var i = 0, len = this.config.length; i < len; i++){
58110             var c = this.config[i];
58111             if(fn.call(scope||this, c, i) === true){
58112                 r[r.length] = c;
58113             }
58114         }
58115         return r;
58116     },
58117
58118     /**
58119      * Returns true if the specified column is sortable.
58120      * @param {Number} col The column index
58121      * @return {Boolean}
58122      */
58123     isSortable : function(col){
58124         if(typeof this.config[col].sortable == "undefined"){
58125             return this.defaultSortable;
58126         }
58127         return this.config[col].sortable;
58128     },
58129
58130     /**
58131      * Returns the rendering (formatting) function defined for the column.
58132      * @param {Number} col The column index.
58133      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58134      */
58135     getRenderer : function(col){
58136         if(!this.config[col].renderer){
58137             return Roo.grid.ColumnModel.defaultRenderer;
58138         }
58139         return this.config[col].renderer;
58140     },
58141
58142     /**
58143      * Sets the rendering (formatting) function for a column.
58144      * @param {Number} col The column index
58145      * @param {Function} fn The function to use to process the cell's raw data
58146      * to return HTML markup for the grid view. The render function is called with
58147      * the following parameters:<ul>
58148      * <li>Data value.</li>
58149      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58150      * <li>css A CSS style string to apply to the table cell.</li>
58151      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58152      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58153      * <li>Row index</li>
58154      * <li>Column index</li>
58155      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58156      */
58157     setRenderer : function(col, fn){
58158         this.config[col].renderer = fn;
58159     },
58160
58161     /**
58162      * Returns the width for the specified column.
58163      * @param {Number} col The column index
58164      * @return {Number}
58165      */
58166     getColumnWidth : function(col){
58167         return this.config[col].width * 1 || this.defaultWidth;
58168     },
58169
58170     /**
58171      * Sets the width for a column.
58172      * @param {Number} col The column index
58173      * @param {Number} width The new width
58174      */
58175     setColumnWidth : function(col, width, suppressEvent){
58176         this.config[col].width = width;
58177         this.totalWidth = null;
58178         if(!suppressEvent){
58179              this.fireEvent("widthchange", this, col, width);
58180         }
58181     },
58182
58183     /**
58184      * Returns the total width of all columns.
58185      * @param {Boolean} includeHidden True to include hidden column widths
58186      * @return {Number}
58187      */
58188     getTotalWidth : function(includeHidden){
58189         if(!this.totalWidth){
58190             this.totalWidth = 0;
58191             for(var i = 0, len = this.config.length; i < len; i++){
58192                 if(includeHidden || !this.isHidden(i)){
58193                     this.totalWidth += this.getColumnWidth(i);
58194                 }
58195             }
58196         }
58197         return this.totalWidth;
58198     },
58199
58200     /**
58201      * Returns the header for the specified column.
58202      * @param {Number} col The column index
58203      * @return {String}
58204      */
58205     getColumnHeader : function(col){
58206         return this.config[col].header;
58207     },
58208
58209     /**
58210      * Sets the header for a column.
58211      * @param {Number} col The column index
58212      * @param {String} header The new header
58213      */
58214     setColumnHeader : function(col, header){
58215         this.config[col].header = header;
58216         this.fireEvent("headerchange", this, col, header);
58217     },
58218
58219     /**
58220      * Returns the tooltip for the specified column.
58221      * @param {Number} col The column index
58222      * @return {String}
58223      */
58224     getColumnTooltip : function(col){
58225             return this.config[col].tooltip;
58226     },
58227     /**
58228      * Sets the tooltip for a column.
58229      * @param {Number} col The column index
58230      * @param {String} tooltip The new tooltip
58231      */
58232     setColumnTooltip : function(col, tooltip){
58233             this.config[col].tooltip = tooltip;
58234     },
58235
58236     /**
58237      * Returns the dataIndex for the specified column.
58238      * @param {Number} col The column index
58239      * @return {Number}
58240      */
58241     getDataIndex : function(col){
58242         return this.config[col].dataIndex;
58243     },
58244
58245     /**
58246      * Sets the dataIndex for a column.
58247      * @param {Number} col The column index
58248      * @param {Number} dataIndex The new dataIndex
58249      */
58250     setDataIndex : function(col, dataIndex){
58251         this.config[col].dataIndex = dataIndex;
58252     },
58253
58254     
58255     
58256     /**
58257      * Returns true if the cell is editable.
58258      * @param {Number} colIndex The column index
58259      * @param {Number} rowIndex The row index - this is nto actually used..?
58260      * @return {Boolean}
58261      */
58262     isCellEditable : function(colIndex, rowIndex){
58263         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58264     },
58265
58266     /**
58267      * Returns the editor defined for the cell/column.
58268      * return false or null to disable editing.
58269      * @param {Number} colIndex The column index
58270      * @param {Number} rowIndex The row index
58271      * @return {Object}
58272      */
58273     getCellEditor : function(colIndex, rowIndex){
58274         return this.config[colIndex].editor;
58275     },
58276
58277     /**
58278      * Sets if a column is editable.
58279      * @param {Number} col The column index
58280      * @param {Boolean} editable True if the column is editable
58281      */
58282     setEditable : function(col, editable){
58283         this.config[col].editable = editable;
58284     },
58285
58286
58287     /**
58288      * Returns true if the column is hidden.
58289      * @param {Number} colIndex The column index
58290      * @return {Boolean}
58291      */
58292     isHidden : function(colIndex){
58293         return this.config[colIndex].hidden;
58294     },
58295
58296
58297     /**
58298      * Returns true if the column width cannot be changed
58299      */
58300     isFixed : function(colIndex){
58301         return this.config[colIndex].fixed;
58302     },
58303
58304     /**
58305      * Returns true if the column can be resized
58306      * @return {Boolean}
58307      */
58308     isResizable : function(colIndex){
58309         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58310     },
58311     /**
58312      * Sets if a column is hidden.
58313      * @param {Number} colIndex The column index
58314      * @param {Boolean} hidden True if the column is hidden
58315      */
58316     setHidden : function(colIndex, hidden){
58317         this.config[colIndex].hidden = hidden;
58318         this.totalWidth = null;
58319         this.fireEvent("hiddenchange", this, colIndex, hidden);
58320     },
58321
58322     /**
58323      * Sets the editor for a column.
58324      * @param {Number} col The column index
58325      * @param {Object} editor The editor object
58326      */
58327     setEditor : function(col, editor){
58328         this.config[col].editor = editor;
58329     }
58330 });
58331
58332 Roo.grid.ColumnModel.defaultRenderer = function(value)
58333 {
58334     if(typeof value == "object") {
58335         return value;
58336     }
58337         if(typeof value == "string" && value.length < 1){
58338             return "&#160;";
58339         }
58340     
58341         return String.format("{0}", value);
58342 };
58343
58344 // Alias for backwards compatibility
58345 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58346 /*
58347  * Based on:
58348  * Ext JS Library 1.1.1
58349  * Copyright(c) 2006-2007, Ext JS, LLC.
58350  *
58351  * Originally Released Under LGPL - original licence link has changed is not relivant.
58352  *
58353  * Fork - LGPL
58354  * <script type="text/javascript">
58355  */
58356
58357 /**
58358  * @class Roo.grid.AbstractSelectionModel
58359  * @extends Roo.util.Observable
58360  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58361  * implemented by descendant classes.  This class should not be directly instantiated.
58362  * @constructor
58363  */
58364 Roo.grid.AbstractSelectionModel = function(){
58365     this.locked = false;
58366     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58367 };
58368
58369 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58370     /** @ignore Called by the grid automatically. Do not call directly. */
58371     init : function(grid){
58372         this.grid = grid;
58373         this.initEvents();
58374     },
58375
58376     /**
58377      * Locks the selections.
58378      */
58379     lock : function(){
58380         this.locked = true;
58381     },
58382
58383     /**
58384      * Unlocks the selections.
58385      */
58386     unlock : function(){
58387         this.locked = false;
58388     },
58389
58390     /**
58391      * Returns true if the selections are locked.
58392      * @return {Boolean}
58393      */
58394     isLocked : function(){
58395         return this.locked;
58396     }
58397 });/*
58398  * Based on:
58399  * Ext JS Library 1.1.1
58400  * Copyright(c) 2006-2007, Ext JS, LLC.
58401  *
58402  * Originally Released Under LGPL - original licence link has changed is not relivant.
58403  *
58404  * Fork - LGPL
58405  * <script type="text/javascript">
58406  */
58407 /**
58408  * @extends Roo.grid.AbstractSelectionModel
58409  * @class Roo.grid.RowSelectionModel
58410  * The default SelectionModel used by {@link Roo.grid.Grid}.
58411  * It supports multiple selections and keyboard selection/navigation. 
58412  * @constructor
58413  * @param {Object} config
58414  */
58415 Roo.grid.RowSelectionModel = function(config){
58416     Roo.apply(this, config);
58417     this.selections = new Roo.util.MixedCollection(false, function(o){
58418         return o.id;
58419     });
58420
58421     this.last = false;
58422     this.lastActive = false;
58423
58424     this.addEvents({
58425         /**
58426              * @event selectionchange
58427              * Fires when the selection changes
58428              * @param {SelectionModel} this
58429              */
58430             "selectionchange" : true,
58431         /**
58432              * @event afterselectionchange
58433              * Fires after the selection changes (eg. by key press or clicking)
58434              * @param {SelectionModel} this
58435              */
58436             "afterselectionchange" : true,
58437         /**
58438              * @event beforerowselect
58439              * Fires when a row is selected being selected, return false to cancel.
58440              * @param {SelectionModel} this
58441              * @param {Number} rowIndex The selected index
58442              * @param {Boolean} keepExisting False if other selections will be cleared
58443              */
58444             "beforerowselect" : true,
58445         /**
58446              * @event rowselect
58447              * Fires when a row is selected.
58448              * @param {SelectionModel} this
58449              * @param {Number} rowIndex The selected index
58450              * @param {Roo.data.Record} r The record
58451              */
58452             "rowselect" : true,
58453         /**
58454              * @event rowdeselect
58455              * Fires when a row is deselected.
58456              * @param {SelectionModel} this
58457              * @param {Number} rowIndex The selected index
58458              */
58459         "rowdeselect" : true
58460     });
58461     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58462     this.locked = false;
58463 };
58464
58465 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58466     /**
58467      * @cfg {Boolean} singleSelect
58468      * True to allow selection of only one row at a time (defaults to false)
58469      */
58470     singleSelect : false,
58471
58472     // private
58473     initEvents : function(){
58474
58475         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58476             this.grid.on("mousedown", this.handleMouseDown, this);
58477         }else{ // allow click to work like normal
58478             this.grid.on("rowclick", this.handleDragableRowClick, this);
58479         }
58480
58481         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58482             "up" : function(e){
58483                 if(!e.shiftKey){
58484                     this.selectPrevious(e.shiftKey);
58485                 }else if(this.last !== false && this.lastActive !== false){
58486                     var last = this.last;
58487                     this.selectRange(this.last,  this.lastActive-1);
58488                     this.grid.getView().focusRow(this.lastActive);
58489                     if(last !== false){
58490                         this.last = last;
58491                     }
58492                 }else{
58493                     this.selectFirstRow();
58494                 }
58495                 this.fireEvent("afterselectionchange", this);
58496             },
58497             "down" : function(e){
58498                 if(!e.shiftKey){
58499                     this.selectNext(e.shiftKey);
58500                 }else if(this.last !== false && this.lastActive !== false){
58501                     var last = this.last;
58502                     this.selectRange(this.last,  this.lastActive+1);
58503                     this.grid.getView().focusRow(this.lastActive);
58504                     if(last !== false){
58505                         this.last = last;
58506                     }
58507                 }else{
58508                     this.selectFirstRow();
58509                 }
58510                 this.fireEvent("afterselectionchange", this);
58511             },
58512             scope: this
58513         });
58514
58515         var view = this.grid.view;
58516         view.on("refresh", this.onRefresh, this);
58517         view.on("rowupdated", this.onRowUpdated, this);
58518         view.on("rowremoved", this.onRemove, this);
58519     },
58520
58521     // private
58522     onRefresh : function(){
58523         var ds = this.grid.dataSource, i, v = this.grid.view;
58524         var s = this.selections;
58525         s.each(function(r){
58526             if((i = ds.indexOfId(r.id)) != -1){
58527                 v.onRowSelect(i);
58528                 s.add(ds.getAt(i)); // updating the selection relate data
58529             }else{
58530                 s.remove(r);
58531             }
58532         });
58533     },
58534
58535     // private
58536     onRemove : function(v, index, r){
58537         this.selections.remove(r);
58538     },
58539
58540     // private
58541     onRowUpdated : function(v, index, r){
58542         if(this.isSelected(r)){
58543             v.onRowSelect(index);
58544         }
58545     },
58546
58547     /**
58548      * Select records.
58549      * @param {Array} records The records to select
58550      * @param {Boolean} keepExisting (optional) True to keep existing selections
58551      */
58552     selectRecords : function(records, keepExisting){
58553         if(!keepExisting){
58554             this.clearSelections();
58555         }
58556         var ds = this.grid.dataSource;
58557         for(var i = 0, len = records.length; i < len; i++){
58558             this.selectRow(ds.indexOf(records[i]), true);
58559         }
58560     },
58561
58562     /**
58563      * Gets the number of selected rows.
58564      * @return {Number}
58565      */
58566     getCount : function(){
58567         return this.selections.length;
58568     },
58569
58570     /**
58571      * Selects the first row in the grid.
58572      */
58573     selectFirstRow : function(){
58574         this.selectRow(0);
58575     },
58576
58577     /**
58578      * Select the last row.
58579      * @param {Boolean} keepExisting (optional) True to keep existing selections
58580      */
58581     selectLastRow : function(keepExisting){
58582         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
58583     },
58584
58585     /**
58586      * Selects the row immediately following the last selected row.
58587      * @param {Boolean} keepExisting (optional) True to keep existing selections
58588      */
58589     selectNext : function(keepExisting){
58590         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
58591             this.selectRow(this.last+1, keepExisting);
58592             this.grid.getView().focusRow(this.last);
58593         }
58594     },
58595
58596     /**
58597      * Selects the row that precedes the last selected row.
58598      * @param {Boolean} keepExisting (optional) True to keep existing selections
58599      */
58600     selectPrevious : function(keepExisting){
58601         if(this.last){
58602             this.selectRow(this.last-1, keepExisting);
58603             this.grid.getView().focusRow(this.last);
58604         }
58605     },
58606
58607     /**
58608      * Returns the selected records
58609      * @return {Array} Array of selected records
58610      */
58611     getSelections : function(){
58612         return [].concat(this.selections.items);
58613     },
58614
58615     /**
58616      * Returns the first selected record.
58617      * @return {Record}
58618      */
58619     getSelected : function(){
58620         return this.selections.itemAt(0);
58621     },
58622
58623
58624     /**
58625      * Clears all selections.
58626      */
58627     clearSelections : function(fast){
58628         if(this.locked) {
58629             return;
58630         }
58631         if(fast !== true){
58632             var ds = this.grid.dataSource;
58633             var s = this.selections;
58634             s.each(function(r){
58635                 this.deselectRow(ds.indexOfId(r.id));
58636             }, this);
58637             s.clear();
58638         }else{
58639             this.selections.clear();
58640         }
58641         this.last = false;
58642     },
58643
58644
58645     /**
58646      * Selects all rows.
58647      */
58648     selectAll : function(){
58649         if(this.locked) {
58650             return;
58651         }
58652         this.selections.clear();
58653         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
58654             this.selectRow(i, true);
58655         }
58656     },
58657
58658     /**
58659      * Returns True if there is a selection.
58660      * @return {Boolean}
58661      */
58662     hasSelection : function(){
58663         return this.selections.length > 0;
58664     },
58665
58666     /**
58667      * Returns True if the specified row is selected.
58668      * @param {Number/Record} record The record or index of the record to check
58669      * @return {Boolean}
58670      */
58671     isSelected : function(index){
58672         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
58673         return (r && this.selections.key(r.id) ? true : false);
58674     },
58675
58676     /**
58677      * Returns True if the specified record id is selected.
58678      * @param {String} id The id of record to check
58679      * @return {Boolean}
58680      */
58681     isIdSelected : function(id){
58682         return (this.selections.key(id) ? true : false);
58683     },
58684
58685     // private
58686     handleMouseDown : function(e, t){
58687         var view = this.grid.getView(), rowIndex;
58688         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
58689             return;
58690         };
58691         if(e.shiftKey && this.last !== false){
58692             var last = this.last;
58693             this.selectRange(last, rowIndex, e.ctrlKey);
58694             this.last = last; // reset the last
58695             view.focusRow(rowIndex);
58696         }else{
58697             var isSelected = this.isSelected(rowIndex);
58698             if(e.button !== 0 && isSelected){
58699                 view.focusRow(rowIndex);
58700             }else if(e.ctrlKey && isSelected){
58701                 this.deselectRow(rowIndex);
58702             }else if(!isSelected){
58703                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
58704                 view.focusRow(rowIndex);
58705             }
58706         }
58707         this.fireEvent("afterselectionchange", this);
58708     },
58709     // private
58710     handleDragableRowClick :  function(grid, rowIndex, e) 
58711     {
58712         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
58713             this.selectRow(rowIndex, false);
58714             grid.view.focusRow(rowIndex);
58715              this.fireEvent("afterselectionchange", this);
58716         }
58717     },
58718     
58719     /**
58720      * Selects multiple rows.
58721      * @param {Array} rows Array of the indexes of the row to select
58722      * @param {Boolean} keepExisting (optional) True to keep existing selections
58723      */
58724     selectRows : function(rows, keepExisting){
58725         if(!keepExisting){
58726             this.clearSelections();
58727         }
58728         for(var i = 0, len = rows.length; i < len; i++){
58729             this.selectRow(rows[i], true);
58730         }
58731     },
58732
58733     /**
58734      * Selects a range of rows. All rows in between startRow and endRow are also selected.
58735      * @param {Number} startRow The index of the first row in the range
58736      * @param {Number} endRow The index of the last row in the range
58737      * @param {Boolean} keepExisting (optional) True to retain existing selections
58738      */
58739     selectRange : function(startRow, endRow, keepExisting){
58740         if(this.locked) {
58741             return;
58742         }
58743         if(!keepExisting){
58744             this.clearSelections();
58745         }
58746         if(startRow <= endRow){
58747             for(var i = startRow; i <= endRow; i++){
58748                 this.selectRow(i, true);
58749             }
58750         }else{
58751             for(var i = startRow; i >= endRow; i--){
58752                 this.selectRow(i, true);
58753             }
58754         }
58755     },
58756
58757     /**
58758      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
58759      * @param {Number} startRow The index of the first row in the range
58760      * @param {Number} endRow The index of the last row in the range
58761      */
58762     deselectRange : function(startRow, endRow, preventViewNotify){
58763         if(this.locked) {
58764             return;
58765         }
58766         for(var i = startRow; i <= endRow; i++){
58767             this.deselectRow(i, preventViewNotify);
58768         }
58769     },
58770
58771     /**
58772      * Selects a row.
58773      * @param {Number} row The index of the row to select
58774      * @param {Boolean} keepExisting (optional) True to keep existing selections
58775      */
58776     selectRow : function(index, keepExisting, preventViewNotify){
58777         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
58778             return;
58779         }
58780         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
58781             if(!keepExisting || this.singleSelect){
58782                 this.clearSelections();
58783             }
58784             var r = this.grid.dataSource.getAt(index);
58785             this.selections.add(r);
58786             this.last = this.lastActive = index;
58787             if(!preventViewNotify){
58788                 this.grid.getView().onRowSelect(index);
58789             }
58790             this.fireEvent("rowselect", this, index, r);
58791             this.fireEvent("selectionchange", this);
58792         }
58793     },
58794
58795     /**
58796      * Deselects a row.
58797      * @param {Number} row The index of the row to deselect
58798      */
58799     deselectRow : function(index, preventViewNotify){
58800         if(this.locked) {
58801             return;
58802         }
58803         if(this.last == index){
58804             this.last = false;
58805         }
58806         if(this.lastActive == index){
58807             this.lastActive = false;
58808         }
58809         var r = this.grid.dataSource.getAt(index);
58810         this.selections.remove(r);
58811         if(!preventViewNotify){
58812             this.grid.getView().onRowDeselect(index);
58813         }
58814         this.fireEvent("rowdeselect", this, index);
58815         this.fireEvent("selectionchange", this);
58816     },
58817
58818     // private
58819     restoreLast : function(){
58820         if(this._last){
58821             this.last = this._last;
58822         }
58823     },
58824
58825     // private
58826     acceptsNav : function(row, col, cm){
58827         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58828     },
58829
58830     // private
58831     onEditorKey : function(field, e){
58832         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
58833         if(k == e.TAB){
58834             e.stopEvent();
58835             ed.completeEdit();
58836             if(e.shiftKey){
58837                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58838             }else{
58839                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58840             }
58841         }else if(k == e.ENTER && !e.ctrlKey){
58842             e.stopEvent();
58843             ed.completeEdit();
58844             if(e.shiftKey){
58845                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
58846             }else{
58847                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
58848             }
58849         }else if(k == e.ESC){
58850             ed.cancelEdit();
58851         }
58852         if(newCell){
58853             g.startEditing(newCell[0], newCell[1]);
58854         }
58855     }
58856 });/*
58857  * Based on:
58858  * Ext JS Library 1.1.1
58859  * Copyright(c) 2006-2007, Ext JS, LLC.
58860  *
58861  * Originally Released Under LGPL - original licence link has changed is not relivant.
58862  *
58863  * Fork - LGPL
58864  * <script type="text/javascript">
58865  */
58866 /**
58867  * @class Roo.grid.CellSelectionModel
58868  * @extends Roo.grid.AbstractSelectionModel
58869  * This class provides the basic implementation for cell selection in a grid.
58870  * @constructor
58871  * @param {Object} config The object containing the configuration of this model.
58872  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
58873  */
58874 Roo.grid.CellSelectionModel = function(config){
58875     Roo.apply(this, config);
58876
58877     this.selection = null;
58878
58879     this.addEvents({
58880         /**
58881              * @event beforerowselect
58882              * Fires before a cell is selected.
58883              * @param {SelectionModel} this
58884              * @param {Number} rowIndex The selected row index
58885              * @param {Number} colIndex The selected cell index
58886              */
58887             "beforecellselect" : true,
58888         /**
58889              * @event cellselect
58890              * Fires when a cell is selected.
58891              * @param {SelectionModel} this
58892              * @param {Number} rowIndex The selected row index
58893              * @param {Number} colIndex The selected cell index
58894              */
58895             "cellselect" : true,
58896         /**
58897              * @event selectionchange
58898              * Fires when the active selection changes.
58899              * @param {SelectionModel} this
58900              * @param {Object} selection null for no selection or an object (o) with two properties
58901                 <ul>
58902                 <li>o.record: the record object for the row the selection is in</li>
58903                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58904                 </ul>
58905              */
58906             "selectionchange" : true,
58907         /**
58908              * @event tabend
58909              * Fires when the tab (or enter) was pressed on the last editable cell
58910              * You can use this to trigger add new row.
58911              * @param {SelectionModel} this
58912              */
58913             "tabend" : true,
58914          /**
58915              * @event beforeeditnext
58916              * Fires before the next editable sell is made active
58917              * You can use this to skip to another cell or fire the tabend
58918              *    if you set cell to false
58919              * @param {Object} eventdata object : { cell : [ row, col ] } 
58920              */
58921             "beforeeditnext" : true
58922     });
58923     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58924 };
58925
58926 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58927     
58928     enter_is_tab: false,
58929
58930     /** @ignore */
58931     initEvents : function(){
58932         this.grid.on("mousedown", this.handleMouseDown, this);
58933         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58934         var view = this.grid.view;
58935         view.on("refresh", this.onViewChange, this);
58936         view.on("rowupdated", this.onRowUpdated, this);
58937         view.on("beforerowremoved", this.clearSelections, this);
58938         view.on("beforerowsinserted", this.clearSelections, this);
58939         if(this.grid.isEditor){
58940             this.grid.on("beforeedit", this.beforeEdit,  this);
58941         }
58942     },
58943
58944         //private
58945     beforeEdit : function(e){
58946         this.select(e.row, e.column, false, true, e.record);
58947     },
58948
58949         //private
58950     onRowUpdated : function(v, index, r){
58951         if(this.selection && this.selection.record == r){
58952             v.onCellSelect(index, this.selection.cell[1]);
58953         }
58954     },
58955
58956         //private
58957     onViewChange : function(){
58958         this.clearSelections(true);
58959     },
58960
58961         /**
58962          * Returns the currently selected cell,.
58963          * @return {Array} The selected cell (row, column) or null if none selected.
58964          */
58965     getSelectedCell : function(){
58966         return this.selection ? this.selection.cell : null;
58967     },
58968
58969     /**
58970      * Clears all selections.
58971      * @param {Boolean} true to prevent the gridview from being notified about the change.
58972      */
58973     clearSelections : function(preventNotify){
58974         var s = this.selection;
58975         if(s){
58976             if(preventNotify !== true){
58977                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58978             }
58979             this.selection = null;
58980             this.fireEvent("selectionchange", this, null);
58981         }
58982     },
58983
58984     /**
58985      * Returns true if there is a selection.
58986      * @return {Boolean}
58987      */
58988     hasSelection : function(){
58989         return this.selection ? true : false;
58990     },
58991
58992     /** @ignore */
58993     handleMouseDown : function(e, t){
58994         var v = this.grid.getView();
58995         if(this.isLocked()){
58996             return;
58997         };
58998         var row = v.findRowIndex(t);
58999         var cell = v.findCellIndex(t);
59000         if(row !== false && cell !== false){
59001             this.select(row, cell);
59002         }
59003     },
59004
59005     /**
59006      * Selects a cell.
59007      * @param {Number} rowIndex
59008      * @param {Number} collIndex
59009      */
59010     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59011         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59012             this.clearSelections();
59013             r = r || this.grid.dataSource.getAt(rowIndex);
59014             this.selection = {
59015                 record : r,
59016                 cell : [rowIndex, colIndex]
59017             };
59018             if(!preventViewNotify){
59019                 var v = this.grid.getView();
59020                 v.onCellSelect(rowIndex, colIndex);
59021                 if(preventFocus !== true){
59022                     v.focusCell(rowIndex, colIndex);
59023                 }
59024             }
59025             this.fireEvent("cellselect", this, rowIndex, colIndex);
59026             this.fireEvent("selectionchange", this, this.selection);
59027         }
59028     },
59029
59030         //private
59031     isSelectable : function(rowIndex, colIndex, cm){
59032         return !cm.isHidden(colIndex);
59033     },
59034
59035     /** @ignore */
59036     handleKeyDown : function(e){
59037         //Roo.log('Cell Sel Model handleKeyDown');
59038         if(!e.isNavKeyPress()){
59039             return;
59040         }
59041         var g = this.grid, s = this.selection;
59042         if(!s){
59043             e.stopEvent();
59044             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59045             if(cell){
59046                 this.select(cell[0], cell[1]);
59047             }
59048             return;
59049         }
59050         var sm = this;
59051         var walk = function(row, col, step){
59052             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59053         };
59054         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59055         var newCell;
59056
59057       
59058
59059         switch(k){
59060             case e.TAB:
59061                 // handled by onEditorKey
59062                 if (g.isEditor && g.editing) {
59063                     return;
59064                 }
59065                 if(e.shiftKey) {
59066                     newCell = walk(r, c-1, -1);
59067                 } else {
59068                     newCell = walk(r, c+1, 1);
59069                 }
59070                 break;
59071             
59072             case e.DOWN:
59073                newCell = walk(r+1, c, 1);
59074                 break;
59075             
59076             case e.UP:
59077                 newCell = walk(r-1, c, -1);
59078                 break;
59079             
59080             case e.RIGHT:
59081                 newCell = walk(r, c+1, 1);
59082                 break;
59083             
59084             case e.LEFT:
59085                 newCell = walk(r, c-1, -1);
59086                 break;
59087             
59088             case e.ENTER:
59089                 
59090                 if(g.isEditor && !g.editing){
59091                    g.startEditing(r, c);
59092                    e.stopEvent();
59093                    return;
59094                 }
59095                 
59096                 
59097              break;
59098         };
59099         if(newCell){
59100             this.select(newCell[0], newCell[1]);
59101             e.stopEvent();
59102             
59103         }
59104     },
59105
59106     acceptsNav : function(row, col, cm){
59107         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59108     },
59109     /**
59110      * Selects a cell.
59111      * @param {Number} field (not used) - as it's normally used as a listener
59112      * @param {Number} e - event - fake it by using
59113      *
59114      * var e = Roo.EventObjectImpl.prototype;
59115      * e.keyCode = e.TAB
59116      *
59117      * 
59118      */
59119     onEditorKey : function(field, e){
59120         
59121         var k = e.getKey(),
59122             newCell,
59123             g = this.grid,
59124             ed = g.activeEditor,
59125             forward = false;
59126         ///Roo.log('onEditorKey' + k);
59127         
59128         
59129         if (this.enter_is_tab && k == e.ENTER) {
59130             k = e.TAB;
59131         }
59132         
59133         if(k == e.TAB){
59134             if(e.shiftKey){
59135                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59136             }else{
59137                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59138                 forward = true;
59139             }
59140             
59141             e.stopEvent();
59142             
59143         } else if(k == e.ENTER &&  !e.ctrlKey){
59144             ed.completeEdit();
59145             e.stopEvent();
59146             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59147         
59148                 } else if(k == e.ESC){
59149             ed.cancelEdit();
59150         }
59151                 
59152         if (newCell) {
59153             var ecall = { cell : newCell, forward : forward };
59154             this.fireEvent('beforeeditnext', ecall );
59155             newCell = ecall.cell;
59156                         forward = ecall.forward;
59157         }
59158                 
59159         if(newCell){
59160             //Roo.log('next cell after edit');
59161             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59162         } else if (forward) {
59163             // tabbed past last
59164             this.fireEvent.defer(100, this, ['tabend',this]);
59165         }
59166     }
59167 });/*
59168  * Based on:
59169  * Ext JS Library 1.1.1
59170  * Copyright(c) 2006-2007, Ext JS, LLC.
59171  *
59172  * Originally Released Under LGPL - original licence link has changed is not relivant.
59173  *
59174  * Fork - LGPL
59175  * <script type="text/javascript">
59176  */
59177  
59178 /**
59179  * @class Roo.grid.EditorGrid
59180  * @extends Roo.grid.Grid
59181  * Class for creating and editable grid.
59182  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59183  * The container MUST have some type of size defined for the grid to fill. The container will be 
59184  * automatically set to position relative if it isn't already.
59185  * @param {Object} dataSource The data model to bind to
59186  * @param {Object} colModel The column model with info about this grid's columns
59187  */
59188 Roo.grid.EditorGrid = function(container, config){
59189     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59190     this.getGridEl().addClass("xedit-grid");
59191
59192     if(!this.selModel){
59193         this.selModel = new Roo.grid.CellSelectionModel();
59194     }
59195
59196     this.activeEditor = null;
59197
59198         this.addEvents({
59199             /**
59200              * @event beforeedit
59201              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59202              * <ul style="padding:5px;padding-left:16px;">
59203              * <li>grid - This grid</li>
59204              * <li>record - The record being edited</li>
59205              * <li>field - The field name being edited</li>
59206              * <li>value - The value for the field being edited.</li>
59207              * <li>row - The grid row index</li>
59208              * <li>column - The grid column index</li>
59209              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59210              * </ul>
59211              * @param {Object} e An edit event (see above for description)
59212              */
59213             "beforeedit" : true,
59214             /**
59215              * @event afteredit
59216              * Fires after a cell is edited. <br />
59217              * <ul style="padding:5px;padding-left:16px;">
59218              * <li>grid - This grid</li>
59219              * <li>record - The record being edited</li>
59220              * <li>field - The field name being edited</li>
59221              * <li>value - The value being set</li>
59222              * <li>originalValue - The original value for the field, before the edit.</li>
59223              * <li>row - The grid row index</li>
59224              * <li>column - The grid column index</li>
59225              * </ul>
59226              * @param {Object} e An edit event (see above for description)
59227              */
59228             "afteredit" : true,
59229             /**
59230              * @event validateedit
59231              * Fires after a cell is edited, but before the value is set in the record. 
59232          * You can use this to modify the value being set in the field, Return false
59233              * to cancel the change. The edit event object has the following properties <br />
59234              * <ul style="padding:5px;padding-left:16px;">
59235          * <li>editor - This editor</li>
59236              * <li>grid - This grid</li>
59237              * <li>record - The record being edited</li>
59238              * <li>field - The field name being edited</li>
59239              * <li>value - The value being set</li>
59240              * <li>originalValue - The original value for the field, before the edit.</li>
59241              * <li>row - The grid row index</li>
59242              * <li>column - The grid column index</li>
59243              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59244              * </ul>
59245              * @param {Object} e An edit event (see above for description)
59246              */
59247             "validateedit" : true
59248         });
59249     this.on("bodyscroll", this.stopEditing,  this);
59250     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59251 };
59252
59253 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59254     /**
59255      * @cfg {Number} clicksToEdit
59256      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59257      */
59258     clicksToEdit: 2,
59259
59260     // private
59261     isEditor : true,
59262     // private
59263     trackMouseOver: false, // causes very odd FF errors
59264
59265     onCellDblClick : function(g, row, col){
59266         this.startEditing(row, col);
59267     },
59268
59269     onEditComplete : function(ed, value, startValue){
59270         this.editing = false;
59271         this.activeEditor = null;
59272         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59273         var r = ed.record;
59274         var field = this.colModel.getDataIndex(ed.col);
59275         var e = {
59276             grid: this,
59277             record: r,
59278             field: field,
59279             originalValue: startValue,
59280             value: value,
59281             row: ed.row,
59282             column: ed.col,
59283             cancel:false,
59284             editor: ed
59285         };
59286         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59287         cell.show();
59288           
59289         if(String(value) !== String(startValue)){
59290             
59291             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59292                 r.set(field, e.value);
59293                 // if we are dealing with a combo box..
59294                 // then we also set the 'name' colum to be the displayField
59295                 if (ed.field.displayField && ed.field.name) {
59296                     r.set(ed.field.name, ed.field.el.dom.value);
59297                 }
59298                 
59299                 delete e.cancel; //?? why!!!
59300                 this.fireEvent("afteredit", e);
59301             }
59302         } else {
59303             this.fireEvent("afteredit", e); // always fire it!
59304         }
59305         this.view.focusCell(ed.row, ed.col);
59306     },
59307
59308     /**
59309      * Starts editing the specified for the specified row/column
59310      * @param {Number} rowIndex
59311      * @param {Number} colIndex
59312      */
59313     startEditing : function(row, col){
59314         this.stopEditing();
59315         if(this.colModel.isCellEditable(col, row)){
59316             this.view.ensureVisible(row, col, true);
59317           
59318             var r = this.dataSource.getAt(row);
59319             var field = this.colModel.getDataIndex(col);
59320             var cell = Roo.get(this.view.getCell(row,col));
59321             var e = {
59322                 grid: this,
59323                 record: r,
59324                 field: field,
59325                 value: r.data[field],
59326                 row: row,
59327                 column: col,
59328                 cancel:false 
59329             };
59330             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59331                 this.editing = true;
59332                 var ed = this.colModel.getCellEditor(col, row);
59333                 
59334                 if (!ed) {
59335                     return;
59336                 }
59337                 if(!ed.rendered){
59338                     ed.render(ed.parentEl || document.body);
59339                 }
59340                 ed.field.reset();
59341                
59342                 cell.hide();
59343                 
59344                 (function(){ // complex but required for focus issues in safari, ie and opera
59345                     ed.row = row;
59346                     ed.col = col;
59347                     ed.record = r;
59348                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59349                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59350                     this.activeEditor = ed;
59351                     var v = r.data[field];
59352                     ed.startEdit(this.view.getCell(row, col), v);
59353                     // combo's with 'displayField and name set
59354                     if (ed.field.displayField && ed.field.name) {
59355                         ed.field.el.dom.value = r.data[ed.field.name];
59356                     }
59357                     
59358                     
59359                 }).defer(50, this);
59360             }
59361         }
59362     },
59363         
59364     /**
59365      * Stops any active editing
59366      */
59367     stopEditing : function(){
59368         if(this.activeEditor){
59369             this.activeEditor.completeEdit();
59370         }
59371         this.activeEditor = null;
59372     },
59373         
59374          /**
59375      * Called to get grid's drag proxy text, by default returns this.ddText.
59376      * @return {String}
59377      */
59378     getDragDropText : function(){
59379         var count = this.selModel.getSelectedCell() ? 1 : 0;
59380         return String.format(this.ddText, count, count == 1 ? '' : 's');
59381     }
59382         
59383 });/*
59384  * Based on:
59385  * Ext JS Library 1.1.1
59386  * Copyright(c) 2006-2007, Ext JS, LLC.
59387  *
59388  * Originally Released Under LGPL - original licence link has changed is not relivant.
59389  *
59390  * Fork - LGPL
59391  * <script type="text/javascript">
59392  */
59393
59394 // private - not really -- you end up using it !
59395 // This is a support class used internally by the Grid components
59396
59397 /**
59398  * @class Roo.grid.GridEditor
59399  * @extends Roo.Editor
59400  * Class for creating and editable grid elements.
59401  * @param {Object} config any settings (must include field)
59402  */
59403 Roo.grid.GridEditor = function(field, config){
59404     if (!config && field.field) {
59405         config = field;
59406         field = Roo.factory(config.field, Roo.form);
59407     }
59408     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59409     field.monitorTab = false;
59410 };
59411
59412 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59413     
59414     /**
59415      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59416      */
59417     
59418     alignment: "tl-tl",
59419     autoSize: "width",
59420     hideEl : false,
59421     cls: "x-small-editor x-grid-editor",
59422     shim:false,
59423     shadow:"frame"
59424 });/*
59425  * Based on:
59426  * Ext JS Library 1.1.1
59427  * Copyright(c) 2006-2007, Ext JS, LLC.
59428  *
59429  * Originally Released Under LGPL - original licence link has changed is not relivant.
59430  *
59431  * Fork - LGPL
59432  * <script type="text/javascript">
59433  */
59434   
59435
59436   
59437 Roo.grid.PropertyRecord = Roo.data.Record.create([
59438     {name:'name',type:'string'},  'value'
59439 ]);
59440
59441
59442 Roo.grid.PropertyStore = function(grid, source){
59443     this.grid = grid;
59444     this.store = new Roo.data.Store({
59445         recordType : Roo.grid.PropertyRecord
59446     });
59447     this.store.on('update', this.onUpdate,  this);
59448     if(source){
59449         this.setSource(source);
59450     }
59451     Roo.grid.PropertyStore.superclass.constructor.call(this);
59452 };
59453
59454
59455
59456 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59457     setSource : function(o){
59458         this.source = o;
59459         this.store.removeAll();
59460         var data = [];
59461         for(var k in o){
59462             if(this.isEditableValue(o[k])){
59463                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59464             }
59465         }
59466         this.store.loadRecords({records: data}, {}, true);
59467     },
59468
59469     onUpdate : function(ds, record, type){
59470         if(type == Roo.data.Record.EDIT){
59471             var v = record.data['value'];
59472             var oldValue = record.modified['value'];
59473             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59474                 this.source[record.id] = v;
59475                 record.commit();
59476                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59477             }else{
59478                 record.reject();
59479             }
59480         }
59481     },
59482
59483     getProperty : function(row){
59484        return this.store.getAt(row);
59485     },
59486
59487     isEditableValue: function(val){
59488         if(val && val instanceof Date){
59489             return true;
59490         }else if(typeof val == 'object' || typeof val == 'function'){
59491             return false;
59492         }
59493         return true;
59494     },
59495
59496     setValue : function(prop, value){
59497         this.source[prop] = value;
59498         this.store.getById(prop).set('value', value);
59499     },
59500
59501     getSource : function(){
59502         return this.source;
59503     }
59504 });
59505
59506 Roo.grid.PropertyColumnModel = function(grid, store){
59507     this.grid = grid;
59508     var g = Roo.grid;
59509     g.PropertyColumnModel.superclass.constructor.call(this, [
59510         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59511         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59512     ]);
59513     this.store = store;
59514     this.bselect = Roo.DomHelper.append(document.body, {
59515         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59516             {tag: 'option', value: 'true', html: 'true'},
59517             {tag: 'option', value: 'false', html: 'false'}
59518         ]
59519     });
59520     Roo.id(this.bselect);
59521     var f = Roo.form;
59522     this.editors = {
59523         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59524         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59525         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59526         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59527         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59528     };
59529     this.renderCellDelegate = this.renderCell.createDelegate(this);
59530     this.renderPropDelegate = this.renderProp.createDelegate(this);
59531 };
59532
59533 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59534     
59535     
59536     nameText : 'Name',
59537     valueText : 'Value',
59538     
59539     dateFormat : 'm/j/Y',
59540     
59541     
59542     renderDate : function(dateVal){
59543         return dateVal.dateFormat(this.dateFormat);
59544     },
59545
59546     renderBool : function(bVal){
59547         return bVal ? 'true' : 'false';
59548     },
59549
59550     isCellEditable : function(colIndex, rowIndex){
59551         return colIndex == 1;
59552     },
59553
59554     getRenderer : function(col){
59555         return col == 1 ?
59556             this.renderCellDelegate : this.renderPropDelegate;
59557     },
59558
59559     renderProp : function(v){
59560         return this.getPropertyName(v);
59561     },
59562
59563     renderCell : function(val){
59564         var rv = val;
59565         if(val instanceof Date){
59566             rv = this.renderDate(val);
59567         }else if(typeof val == 'boolean'){
59568             rv = this.renderBool(val);
59569         }
59570         return Roo.util.Format.htmlEncode(rv);
59571     },
59572
59573     getPropertyName : function(name){
59574         var pn = this.grid.propertyNames;
59575         return pn && pn[name] ? pn[name] : name;
59576     },
59577
59578     getCellEditor : function(colIndex, rowIndex){
59579         var p = this.store.getProperty(rowIndex);
59580         var n = p.data['name'], val = p.data['value'];
59581         
59582         if(typeof(this.grid.customEditors[n]) == 'string'){
59583             return this.editors[this.grid.customEditors[n]];
59584         }
59585         if(typeof(this.grid.customEditors[n]) != 'undefined'){
59586             return this.grid.customEditors[n];
59587         }
59588         if(val instanceof Date){
59589             return this.editors['date'];
59590         }else if(typeof val == 'number'){
59591             return this.editors['number'];
59592         }else if(typeof val == 'boolean'){
59593             return this.editors['boolean'];
59594         }else{
59595             return this.editors['string'];
59596         }
59597     }
59598 });
59599
59600 /**
59601  * @class Roo.grid.PropertyGrid
59602  * @extends Roo.grid.EditorGrid
59603  * This class represents the  interface of a component based property grid control.
59604  * <br><br>Usage:<pre><code>
59605  var grid = new Roo.grid.PropertyGrid("my-container-id", {
59606       
59607  });
59608  // set any options
59609  grid.render();
59610  * </code></pre>
59611   
59612  * @constructor
59613  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59614  * The container MUST have some type of size defined for the grid to fill. The container will be
59615  * automatically set to position relative if it isn't already.
59616  * @param {Object} config A config object that sets properties on this grid.
59617  */
59618 Roo.grid.PropertyGrid = function(container, config){
59619     config = config || {};
59620     var store = new Roo.grid.PropertyStore(this);
59621     this.store = store;
59622     var cm = new Roo.grid.PropertyColumnModel(this, store);
59623     store.store.sort('name', 'ASC');
59624     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
59625         ds: store.store,
59626         cm: cm,
59627         enableColLock:false,
59628         enableColumnMove:false,
59629         stripeRows:false,
59630         trackMouseOver: false,
59631         clicksToEdit:1
59632     }, config));
59633     this.getGridEl().addClass('x-props-grid');
59634     this.lastEditRow = null;
59635     this.on('columnresize', this.onColumnResize, this);
59636     this.addEvents({
59637          /**
59638              * @event beforepropertychange
59639              * Fires before a property changes (return false to stop?)
59640              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59641              * @param {String} id Record Id
59642              * @param {String} newval New Value
59643          * @param {String} oldval Old Value
59644              */
59645         "beforepropertychange": true,
59646         /**
59647              * @event propertychange
59648              * Fires after a property changes
59649              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59650              * @param {String} id Record Id
59651              * @param {String} newval New Value
59652          * @param {String} oldval Old Value
59653              */
59654         "propertychange": true
59655     });
59656     this.customEditors = this.customEditors || {};
59657 };
59658 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
59659     
59660      /**
59661      * @cfg {Object} customEditors map of colnames=> custom editors.
59662      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
59663      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
59664      * false disables editing of the field.
59665          */
59666     
59667       /**
59668      * @cfg {Object} propertyNames map of property Names to their displayed value
59669          */
59670     
59671     render : function(){
59672         Roo.grid.PropertyGrid.superclass.render.call(this);
59673         this.autoSize.defer(100, this);
59674     },
59675
59676     autoSize : function(){
59677         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
59678         if(this.view){
59679             this.view.fitColumns();
59680         }
59681     },
59682
59683     onColumnResize : function(){
59684         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
59685         this.autoSize();
59686     },
59687     /**
59688      * Sets the data for the Grid
59689      * accepts a Key => Value object of all the elements avaiable.
59690      * @param {Object} data  to appear in grid.
59691      */
59692     setSource : function(source){
59693         this.store.setSource(source);
59694         //this.autoSize();
59695     },
59696     /**
59697      * Gets all the data from the grid.
59698      * @return {Object} data  data stored in grid
59699      */
59700     getSource : function(){
59701         return this.store.getSource();
59702     }
59703 });/*
59704   
59705  * Licence LGPL
59706  
59707  */
59708  
59709 /**
59710  * @class Roo.grid.Calendar
59711  * @extends Roo.util.Grid
59712  * This class extends the Grid to provide a calendar widget
59713  * <br><br>Usage:<pre><code>
59714  var grid = new Roo.grid.Calendar("my-container-id", {
59715      ds: myDataStore,
59716      cm: myColModel,
59717      selModel: mySelectionModel,
59718      autoSizeColumns: true,
59719      monitorWindowResize: false,
59720      trackMouseOver: true
59721      eventstore : real data store..
59722  });
59723  // set any options
59724  grid.render();
59725   
59726   * @constructor
59727  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59728  * The container MUST have some type of size defined for the grid to fill. The container will be
59729  * automatically set to position relative if it isn't already.
59730  * @param {Object} config A config object that sets properties on this grid.
59731  */
59732 Roo.grid.Calendar = function(container, config){
59733         // initialize the container
59734         this.container = Roo.get(container);
59735         this.container.update("");
59736         this.container.setStyle("overflow", "hidden");
59737     this.container.addClass('x-grid-container');
59738
59739     this.id = this.container.id;
59740
59741     Roo.apply(this, config);
59742     // check and correct shorthanded configs
59743     
59744     var rows = [];
59745     var d =1;
59746     for (var r = 0;r < 6;r++) {
59747         
59748         rows[r]=[];
59749         for (var c =0;c < 7;c++) {
59750             rows[r][c]= '';
59751         }
59752     }
59753     if (this.eventStore) {
59754         this.eventStore= Roo.factory(this.eventStore, Roo.data);
59755         this.eventStore.on('load',this.onLoad, this);
59756         this.eventStore.on('beforeload',this.clearEvents, this);
59757          
59758     }
59759     
59760     this.dataSource = new Roo.data.Store({
59761             proxy: new Roo.data.MemoryProxy(rows),
59762             reader: new Roo.data.ArrayReader({}, [
59763                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
59764     });
59765
59766     this.dataSource.load();
59767     this.ds = this.dataSource;
59768     this.ds.xmodule = this.xmodule || false;
59769     
59770     
59771     var cellRender = function(v,x,r)
59772     {
59773         return String.format(
59774             '<div class="fc-day  fc-widget-content"><div>' +
59775                 '<div class="fc-event-container"></div>' +
59776                 '<div class="fc-day-number">{0}</div>'+
59777                 
59778                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
59779             '</div></div>', v);
59780     
59781     }
59782     
59783     
59784     this.colModel = new Roo.grid.ColumnModel( [
59785         {
59786             xtype: 'ColumnModel',
59787             xns: Roo.grid,
59788             dataIndex : 'weekday0',
59789             header : 'Sunday',
59790             renderer : cellRender
59791         },
59792         {
59793             xtype: 'ColumnModel',
59794             xns: Roo.grid,
59795             dataIndex : 'weekday1',
59796             header : 'Monday',
59797             renderer : cellRender
59798         },
59799         {
59800             xtype: 'ColumnModel',
59801             xns: Roo.grid,
59802             dataIndex : 'weekday2',
59803             header : 'Tuesday',
59804             renderer : cellRender
59805         },
59806         {
59807             xtype: 'ColumnModel',
59808             xns: Roo.grid,
59809             dataIndex : 'weekday3',
59810             header : 'Wednesday',
59811             renderer : cellRender
59812         },
59813         {
59814             xtype: 'ColumnModel',
59815             xns: Roo.grid,
59816             dataIndex : 'weekday4',
59817             header : 'Thursday',
59818             renderer : cellRender
59819         },
59820         {
59821             xtype: 'ColumnModel',
59822             xns: Roo.grid,
59823             dataIndex : 'weekday5',
59824             header : 'Friday',
59825             renderer : cellRender
59826         },
59827         {
59828             xtype: 'ColumnModel',
59829             xns: Roo.grid,
59830             dataIndex : 'weekday6',
59831             header : 'Saturday',
59832             renderer : cellRender
59833         }
59834     ]);
59835     this.cm = this.colModel;
59836     this.cm.xmodule = this.xmodule || false;
59837  
59838         
59839           
59840     //this.selModel = new Roo.grid.CellSelectionModel();
59841     //this.sm = this.selModel;
59842     //this.selModel.init(this);
59843     
59844     
59845     if(this.width){
59846         this.container.setWidth(this.width);
59847     }
59848
59849     if(this.height){
59850         this.container.setHeight(this.height);
59851     }
59852     /** @private */
59853         this.addEvents({
59854         // raw events
59855         /**
59856          * @event click
59857          * The raw click event for the entire grid.
59858          * @param {Roo.EventObject} e
59859          */
59860         "click" : true,
59861         /**
59862          * @event dblclick
59863          * The raw dblclick event for the entire grid.
59864          * @param {Roo.EventObject} e
59865          */
59866         "dblclick" : true,
59867         /**
59868          * @event contextmenu
59869          * The raw contextmenu event for the entire grid.
59870          * @param {Roo.EventObject} e
59871          */
59872         "contextmenu" : true,
59873         /**
59874          * @event mousedown
59875          * The raw mousedown event for the entire grid.
59876          * @param {Roo.EventObject} e
59877          */
59878         "mousedown" : true,
59879         /**
59880          * @event mouseup
59881          * The raw mouseup event for the entire grid.
59882          * @param {Roo.EventObject} e
59883          */
59884         "mouseup" : true,
59885         /**
59886          * @event mouseover
59887          * The raw mouseover event for the entire grid.
59888          * @param {Roo.EventObject} e
59889          */
59890         "mouseover" : true,
59891         /**
59892          * @event mouseout
59893          * The raw mouseout event for the entire grid.
59894          * @param {Roo.EventObject} e
59895          */
59896         "mouseout" : true,
59897         /**
59898          * @event keypress
59899          * The raw keypress event for the entire grid.
59900          * @param {Roo.EventObject} e
59901          */
59902         "keypress" : true,
59903         /**
59904          * @event keydown
59905          * The raw keydown event for the entire grid.
59906          * @param {Roo.EventObject} e
59907          */
59908         "keydown" : true,
59909
59910         // custom events
59911
59912         /**
59913          * @event cellclick
59914          * Fires when a cell is clicked
59915          * @param {Grid} this
59916          * @param {Number} rowIndex
59917          * @param {Number} columnIndex
59918          * @param {Roo.EventObject} e
59919          */
59920         "cellclick" : true,
59921         /**
59922          * @event celldblclick
59923          * Fires when a cell is double clicked
59924          * @param {Grid} this
59925          * @param {Number} rowIndex
59926          * @param {Number} columnIndex
59927          * @param {Roo.EventObject} e
59928          */
59929         "celldblclick" : true,
59930         /**
59931          * @event rowclick
59932          * Fires when a row is clicked
59933          * @param {Grid} this
59934          * @param {Number} rowIndex
59935          * @param {Roo.EventObject} e
59936          */
59937         "rowclick" : true,
59938         /**
59939          * @event rowdblclick
59940          * Fires when a row is double clicked
59941          * @param {Grid} this
59942          * @param {Number} rowIndex
59943          * @param {Roo.EventObject} e
59944          */
59945         "rowdblclick" : true,
59946         /**
59947          * @event headerclick
59948          * Fires when a header is clicked
59949          * @param {Grid} this
59950          * @param {Number} columnIndex
59951          * @param {Roo.EventObject} e
59952          */
59953         "headerclick" : true,
59954         /**
59955          * @event headerdblclick
59956          * Fires when a header cell is double clicked
59957          * @param {Grid} this
59958          * @param {Number} columnIndex
59959          * @param {Roo.EventObject} e
59960          */
59961         "headerdblclick" : true,
59962         /**
59963          * @event rowcontextmenu
59964          * Fires when a row is right clicked
59965          * @param {Grid} this
59966          * @param {Number} rowIndex
59967          * @param {Roo.EventObject} e
59968          */
59969         "rowcontextmenu" : true,
59970         /**
59971          * @event cellcontextmenu
59972          * Fires when a cell is right clicked
59973          * @param {Grid} this
59974          * @param {Number} rowIndex
59975          * @param {Number} cellIndex
59976          * @param {Roo.EventObject} e
59977          */
59978          "cellcontextmenu" : true,
59979         /**
59980          * @event headercontextmenu
59981          * Fires when a header is right clicked
59982          * @param {Grid} this
59983          * @param {Number} columnIndex
59984          * @param {Roo.EventObject} e
59985          */
59986         "headercontextmenu" : true,
59987         /**
59988          * @event bodyscroll
59989          * Fires when the body element is scrolled
59990          * @param {Number} scrollLeft
59991          * @param {Number} scrollTop
59992          */
59993         "bodyscroll" : true,
59994         /**
59995          * @event columnresize
59996          * Fires when the user resizes a column
59997          * @param {Number} columnIndex
59998          * @param {Number} newSize
59999          */
60000         "columnresize" : true,
60001         /**
60002          * @event columnmove
60003          * Fires when the user moves a column
60004          * @param {Number} oldIndex
60005          * @param {Number} newIndex
60006          */
60007         "columnmove" : true,
60008         /**
60009          * @event startdrag
60010          * Fires when row(s) start being dragged
60011          * @param {Grid} this
60012          * @param {Roo.GridDD} dd The drag drop object
60013          * @param {event} e The raw browser event
60014          */
60015         "startdrag" : true,
60016         /**
60017          * @event enddrag
60018          * Fires when a drag operation is complete
60019          * @param {Grid} this
60020          * @param {Roo.GridDD} dd The drag drop object
60021          * @param {event} e The raw browser event
60022          */
60023         "enddrag" : true,
60024         /**
60025          * @event dragdrop
60026          * Fires when dragged row(s) are dropped on a valid DD target
60027          * @param {Grid} this
60028          * @param {Roo.GridDD} dd The drag drop object
60029          * @param {String} targetId The target drag drop object
60030          * @param {event} e The raw browser event
60031          */
60032         "dragdrop" : true,
60033         /**
60034          * @event dragover
60035          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60036          * @param {Grid} this
60037          * @param {Roo.GridDD} dd The drag drop object
60038          * @param {String} targetId The target drag drop object
60039          * @param {event} e The raw browser event
60040          */
60041         "dragover" : true,
60042         /**
60043          * @event dragenter
60044          *  Fires when the dragged row(s) first cross another DD target while being dragged
60045          * @param {Grid} this
60046          * @param {Roo.GridDD} dd The drag drop object
60047          * @param {String} targetId The target drag drop object
60048          * @param {event} e The raw browser event
60049          */
60050         "dragenter" : true,
60051         /**
60052          * @event dragout
60053          * Fires when the dragged row(s) leave another DD target while being dragged
60054          * @param {Grid} this
60055          * @param {Roo.GridDD} dd The drag drop object
60056          * @param {String} targetId The target drag drop object
60057          * @param {event} e The raw browser event
60058          */
60059         "dragout" : true,
60060         /**
60061          * @event rowclass
60062          * Fires when a row is rendered, so you can change add a style to it.
60063          * @param {GridView} gridview   The grid view
60064          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60065          */
60066         'rowclass' : true,
60067
60068         /**
60069          * @event render
60070          * Fires when the grid is rendered
60071          * @param {Grid} grid
60072          */
60073         'render' : true,
60074             /**
60075              * @event select
60076              * Fires when a date is selected
60077              * @param {DatePicker} this
60078              * @param {Date} date The selected date
60079              */
60080         'select': true,
60081         /**
60082              * @event monthchange
60083              * Fires when the displayed month changes 
60084              * @param {DatePicker} this
60085              * @param {Date} date The selected month
60086              */
60087         'monthchange': true,
60088         /**
60089              * @event evententer
60090              * Fires when mouse over an event
60091              * @param {Calendar} this
60092              * @param {event} Event
60093              */
60094         'evententer': true,
60095         /**
60096              * @event eventleave
60097              * Fires when the mouse leaves an
60098              * @param {Calendar} this
60099              * @param {event}
60100              */
60101         'eventleave': true,
60102         /**
60103              * @event eventclick
60104              * Fires when the mouse click an
60105              * @param {Calendar} this
60106              * @param {event}
60107              */
60108         'eventclick': true,
60109         /**
60110              * @event eventrender
60111              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60112              * @param {Calendar} this
60113              * @param {data} data to be modified
60114              */
60115         'eventrender': true
60116         
60117     });
60118
60119     Roo.grid.Grid.superclass.constructor.call(this);
60120     this.on('render', function() {
60121         this.view.el.addClass('x-grid-cal'); 
60122         
60123         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60124
60125     },this);
60126     
60127     if (!Roo.grid.Calendar.style) {
60128         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60129             
60130             
60131             '.x-grid-cal .x-grid-col' :  {
60132                 height: 'auto !important',
60133                 'vertical-align': 'top'
60134             },
60135             '.x-grid-cal  .fc-event-hori' : {
60136                 height: '14px'
60137             }
60138              
60139             
60140         }, Roo.id());
60141     }
60142
60143     
60144     
60145 };
60146 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60147     /**
60148      * @cfg {Store} eventStore The store that loads events.
60149      */
60150     eventStore : 25,
60151
60152      
60153     activeDate : false,
60154     startDay : 0,
60155     autoWidth : true,
60156     monitorWindowResize : false,
60157
60158     
60159     resizeColumns : function() {
60160         var col = (this.view.el.getWidth() / 7) - 3;
60161         // loop through cols, and setWidth
60162         for(var i =0 ; i < 7 ; i++){
60163             this.cm.setColumnWidth(i, col);
60164         }
60165     },
60166      setDate :function(date) {
60167         
60168         Roo.log('setDate?');
60169         
60170         this.resizeColumns();
60171         var vd = this.activeDate;
60172         this.activeDate = date;
60173 //        if(vd && this.el){
60174 //            var t = date.getTime();
60175 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60176 //                Roo.log('using add remove');
60177 //                
60178 //                this.fireEvent('monthchange', this, date);
60179 //                
60180 //                this.cells.removeClass("fc-state-highlight");
60181 //                this.cells.each(function(c){
60182 //                   if(c.dateValue == t){
60183 //                       c.addClass("fc-state-highlight");
60184 //                       setTimeout(function(){
60185 //                            try{c.dom.firstChild.focus();}catch(e){}
60186 //                       }, 50);
60187 //                       return false;
60188 //                   }
60189 //                   return true;
60190 //                });
60191 //                return;
60192 //            }
60193 //        }
60194         
60195         var days = date.getDaysInMonth();
60196         
60197         var firstOfMonth = date.getFirstDateOfMonth();
60198         var startingPos = firstOfMonth.getDay()-this.startDay;
60199         
60200         if(startingPos < this.startDay){
60201             startingPos += 7;
60202         }
60203         
60204         var pm = date.add(Date.MONTH, -1);
60205         var prevStart = pm.getDaysInMonth()-startingPos;
60206 //        
60207         
60208         
60209         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60210         
60211         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60212         //this.cells.addClassOnOver('fc-state-hover');
60213         
60214         var cells = this.cells.elements;
60215         var textEls = this.textNodes;
60216         
60217         //Roo.each(cells, function(cell){
60218         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60219         //});
60220         
60221         days += startingPos;
60222
60223         // convert everything to numbers so it's fast
60224         var day = 86400000;
60225         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60226         //Roo.log(d);
60227         //Roo.log(pm);
60228         //Roo.log(prevStart);
60229         
60230         var today = new Date().clearTime().getTime();
60231         var sel = date.clearTime().getTime();
60232         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60233         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60234         var ddMatch = this.disabledDatesRE;
60235         var ddText = this.disabledDatesText;
60236         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60237         var ddaysText = this.disabledDaysText;
60238         var format = this.format;
60239         
60240         var setCellClass = function(cal, cell){
60241             
60242             //Roo.log('set Cell Class');
60243             cell.title = "";
60244             var t = d.getTime();
60245             
60246             //Roo.log(d);
60247             
60248             
60249             cell.dateValue = t;
60250             if(t == today){
60251                 cell.className += " fc-today";
60252                 cell.className += " fc-state-highlight";
60253                 cell.title = cal.todayText;
60254             }
60255             if(t == sel){
60256                 // disable highlight in other month..
60257                 cell.className += " fc-state-highlight";
60258                 
60259             }
60260             // disabling
60261             if(t < min) {
60262                 //cell.className = " fc-state-disabled";
60263                 cell.title = cal.minText;
60264                 return;
60265             }
60266             if(t > max) {
60267                 //cell.className = " fc-state-disabled";
60268                 cell.title = cal.maxText;
60269                 return;
60270             }
60271             if(ddays){
60272                 if(ddays.indexOf(d.getDay()) != -1){
60273                     // cell.title = ddaysText;
60274                    // cell.className = " fc-state-disabled";
60275                 }
60276             }
60277             if(ddMatch && format){
60278                 var fvalue = d.dateFormat(format);
60279                 if(ddMatch.test(fvalue)){
60280                     cell.title = ddText.replace("%0", fvalue);
60281                    cell.className = " fc-state-disabled";
60282                 }
60283             }
60284             
60285             if (!cell.initialClassName) {
60286                 cell.initialClassName = cell.dom.className;
60287             }
60288             
60289             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60290         };
60291
60292         var i = 0;
60293         
60294         for(; i < startingPos; i++) {
60295             cells[i].dayName =  (++prevStart);
60296             Roo.log(textEls[i]);
60297             d.setDate(d.getDate()+1);
60298             
60299             //cells[i].className = "fc-past fc-other-month";
60300             setCellClass(this, cells[i]);
60301         }
60302         
60303         var intDay = 0;
60304         
60305         for(; i < days; i++){
60306             intDay = i - startingPos + 1;
60307             cells[i].dayName =  (intDay);
60308             d.setDate(d.getDate()+1);
60309             
60310             cells[i].className = ''; // "x-date-active";
60311             setCellClass(this, cells[i]);
60312         }
60313         var extraDays = 0;
60314         
60315         for(; i < 42; i++) {
60316             //textEls[i].innerHTML = (++extraDays);
60317             
60318             d.setDate(d.getDate()+1);
60319             cells[i].dayName = (++extraDays);
60320             cells[i].className = "fc-future fc-other-month";
60321             setCellClass(this, cells[i]);
60322         }
60323         
60324         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60325         
60326         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60327         
60328         // this will cause all the cells to mis
60329         var rows= [];
60330         var i =0;
60331         for (var r = 0;r < 6;r++) {
60332             for (var c =0;c < 7;c++) {
60333                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60334             }    
60335         }
60336         
60337         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60338         for(i=0;i<cells.length;i++) {
60339             
60340             this.cells.elements[i].dayName = cells[i].dayName ;
60341             this.cells.elements[i].className = cells[i].className;
60342             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60343             this.cells.elements[i].title = cells[i].title ;
60344             this.cells.elements[i].dateValue = cells[i].dateValue ;
60345         }
60346         
60347         
60348         
60349         
60350         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60351         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60352         
60353         ////if(totalRows != 6){
60354             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60355            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60356        // }
60357         
60358         this.fireEvent('monthchange', this, date);
60359         
60360         
60361     },
60362  /**
60363      * Returns the grid's SelectionModel.
60364      * @return {SelectionModel}
60365      */
60366     getSelectionModel : function(){
60367         if(!this.selModel){
60368             this.selModel = new Roo.grid.CellSelectionModel();
60369         }
60370         return this.selModel;
60371     },
60372
60373     load: function() {
60374         this.eventStore.load()
60375         
60376         
60377         
60378     },
60379     
60380     findCell : function(dt) {
60381         dt = dt.clearTime().getTime();
60382         var ret = false;
60383         this.cells.each(function(c){
60384             //Roo.log("check " +c.dateValue + '?=' + dt);
60385             if(c.dateValue == dt){
60386                 ret = c;
60387                 return false;
60388             }
60389             return true;
60390         });
60391         
60392         return ret;
60393     },
60394     
60395     findCells : function(rec) {
60396         var s = rec.data.start_dt.clone().clearTime().getTime();
60397        // Roo.log(s);
60398         var e= rec.data.end_dt.clone().clearTime().getTime();
60399        // Roo.log(e);
60400         var ret = [];
60401         this.cells.each(function(c){
60402              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60403             
60404             if(c.dateValue > e){
60405                 return ;
60406             }
60407             if(c.dateValue < s){
60408                 return ;
60409             }
60410             ret.push(c);
60411         });
60412         
60413         return ret;    
60414     },
60415     
60416     findBestRow: function(cells)
60417     {
60418         var ret = 0;
60419         
60420         for (var i =0 ; i < cells.length;i++) {
60421             ret  = Math.max(cells[i].rows || 0,ret);
60422         }
60423         return ret;
60424         
60425     },
60426     
60427     
60428     addItem : function(rec)
60429     {
60430         // look for vertical location slot in
60431         var cells = this.findCells(rec);
60432         
60433         rec.row = this.findBestRow(cells);
60434         
60435         // work out the location.
60436         
60437         var crow = false;
60438         var rows = [];
60439         for(var i =0; i < cells.length; i++) {
60440             if (!crow) {
60441                 crow = {
60442                     start : cells[i],
60443                     end :  cells[i]
60444                 };
60445                 continue;
60446             }
60447             if (crow.start.getY() == cells[i].getY()) {
60448                 // on same row.
60449                 crow.end = cells[i];
60450                 continue;
60451             }
60452             // different row.
60453             rows.push(crow);
60454             crow = {
60455                 start: cells[i],
60456                 end : cells[i]
60457             };
60458             
60459         }
60460         
60461         rows.push(crow);
60462         rec.els = [];
60463         rec.rows = rows;
60464         rec.cells = cells;
60465         for (var i = 0; i < cells.length;i++) {
60466             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60467             
60468         }
60469         
60470         
60471     },
60472     
60473     clearEvents: function() {
60474         
60475         if (!this.eventStore.getCount()) {
60476             return;
60477         }
60478         // reset number of rows in cells.
60479         Roo.each(this.cells.elements, function(c){
60480             c.rows = 0;
60481         });
60482         
60483         this.eventStore.each(function(e) {
60484             this.clearEvent(e);
60485         },this);
60486         
60487     },
60488     
60489     clearEvent : function(ev)
60490     {
60491         if (ev.els) {
60492             Roo.each(ev.els, function(el) {
60493                 el.un('mouseenter' ,this.onEventEnter, this);
60494                 el.un('mouseleave' ,this.onEventLeave, this);
60495                 el.remove();
60496             },this);
60497             ev.els = [];
60498         }
60499     },
60500     
60501     
60502     renderEvent : function(ev,ctr) {
60503         if (!ctr) {
60504              ctr = this.view.el.select('.fc-event-container',true).first();
60505         }
60506         
60507          
60508         this.clearEvent(ev);
60509             //code
60510        
60511         
60512         
60513         ev.els = [];
60514         var cells = ev.cells;
60515         var rows = ev.rows;
60516         this.fireEvent('eventrender', this, ev);
60517         
60518         for(var i =0; i < rows.length; i++) {
60519             
60520             cls = '';
60521             if (i == 0) {
60522                 cls += ' fc-event-start';
60523             }
60524             if ((i+1) == rows.length) {
60525                 cls += ' fc-event-end';
60526             }
60527             
60528             //Roo.log(ev.data);
60529             // how many rows should it span..
60530             var cg = this.eventTmpl.append(ctr,Roo.apply({
60531                 fccls : cls
60532                 
60533             }, ev.data) , true);
60534             
60535             
60536             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60537             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60538             cg.on('click', this.onEventClick, this, ev);
60539             
60540             ev.els.push(cg);
60541             
60542             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60543             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60544             //Roo.log(cg);
60545              
60546             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60547             cg.setWidth(ebox.right - sbox.x -2);
60548         }
60549     },
60550     
60551     renderEvents: function()
60552     {   
60553         // first make sure there is enough space..
60554         
60555         if (!this.eventTmpl) {
60556             this.eventTmpl = new Roo.Template(
60557                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60558                     '<div class="fc-event-inner">' +
60559                         '<span class="fc-event-time">{time}</span>' +
60560                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60561                     '</div>' +
60562                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60563                 '</div>'
60564             );
60565                 
60566         }
60567                
60568         
60569         
60570         this.cells.each(function(c) {
60571             //Roo.log(c.select('.fc-day-content div',true).first());
60572             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60573         });
60574         
60575         var ctr = this.view.el.select('.fc-event-container',true).first();
60576         
60577         var cls;
60578         this.eventStore.each(function(ev){
60579             
60580             this.renderEvent(ev);
60581              
60582              
60583         }, this);
60584         this.view.layout();
60585         
60586     },
60587     
60588     onEventEnter: function (e, el,event,d) {
60589         this.fireEvent('evententer', this, el, event);
60590     },
60591     
60592     onEventLeave: function (e, el,event,d) {
60593         this.fireEvent('eventleave', this, el, event);
60594     },
60595     
60596     onEventClick: function (e, el,event,d) {
60597         this.fireEvent('eventclick', this, el, event);
60598     },
60599     
60600     onMonthChange: function () {
60601         this.store.load();
60602     },
60603     
60604     onLoad: function () {
60605         
60606         //Roo.log('calendar onload');
60607 //         
60608         if(this.eventStore.getCount() > 0){
60609             
60610            
60611             
60612             this.eventStore.each(function(d){
60613                 
60614                 
60615                 // FIXME..
60616                 var add =   d.data;
60617                 if (typeof(add.end_dt) == 'undefined')  {
60618                     Roo.log("Missing End time in calendar data: ");
60619                     Roo.log(d);
60620                     return;
60621                 }
60622                 if (typeof(add.start_dt) == 'undefined')  {
60623                     Roo.log("Missing Start time in calendar data: ");
60624                     Roo.log(d);
60625                     return;
60626                 }
60627                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
60628                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
60629                 add.id = add.id || d.id;
60630                 add.title = add.title || '??';
60631                 
60632                 this.addItem(d);
60633                 
60634              
60635             },this);
60636         }
60637         
60638         this.renderEvents();
60639     }
60640     
60641
60642 });
60643 /*
60644  grid : {
60645                 xtype: 'Grid',
60646                 xns: Roo.grid,
60647                 listeners : {
60648                     render : function ()
60649                     {
60650                         _this.grid = this;
60651                         
60652                         if (!this.view.el.hasClass('course-timesheet')) {
60653                             this.view.el.addClass('course-timesheet');
60654                         }
60655                         if (this.tsStyle) {
60656                             this.ds.load({});
60657                             return; 
60658                         }
60659                         Roo.log('width');
60660                         Roo.log(_this.grid.view.el.getWidth());
60661                         
60662                         
60663                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
60664                             '.course-timesheet .x-grid-row' : {
60665                                 height: '80px'
60666                             },
60667                             '.x-grid-row td' : {
60668                                 'vertical-align' : 0
60669                             },
60670                             '.course-edit-link' : {
60671                                 'color' : 'blue',
60672                                 'text-overflow' : 'ellipsis',
60673                                 'overflow' : 'hidden',
60674                                 'white-space' : 'nowrap',
60675                                 'cursor' : 'pointer'
60676                             },
60677                             '.sub-link' : {
60678                                 'color' : 'green'
60679                             },
60680                             '.de-act-sup-link' : {
60681                                 'color' : 'purple',
60682                                 'text-decoration' : 'line-through'
60683                             },
60684                             '.de-act-link' : {
60685                                 'color' : 'red',
60686                                 'text-decoration' : 'line-through'
60687                             },
60688                             '.course-timesheet .course-highlight' : {
60689                                 'border-top-style': 'dashed !important',
60690                                 'border-bottom-bottom': 'dashed !important'
60691                             },
60692                             '.course-timesheet .course-item' : {
60693                                 'font-family'   : 'tahoma, arial, helvetica',
60694                                 'font-size'     : '11px',
60695                                 'overflow'      : 'hidden',
60696                                 'padding-left'  : '10px',
60697                                 'padding-right' : '10px',
60698                                 'padding-top' : '10px' 
60699                             }
60700                             
60701                         }, Roo.id());
60702                                 this.ds.load({});
60703                     }
60704                 },
60705                 autoWidth : true,
60706                 monitorWindowResize : false,
60707                 cellrenderer : function(v,x,r)
60708                 {
60709                     return v;
60710                 },
60711                 sm : {
60712                     xtype: 'CellSelectionModel',
60713                     xns: Roo.grid
60714                 },
60715                 dataSource : {
60716                     xtype: 'Store',
60717                     xns: Roo.data,
60718                     listeners : {
60719                         beforeload : function (_self, options)
60720                         {
60721                             options.params = options.params || {};
60722                             options.params._month = _this.monthField.getValue();
60723                             options.params.limit = 9999;
60724                             options.params['sort'] = 'when_dt';    
60725                             options.params['dir'] = 'ASC';    
60726                             this.proxy.loadResponse = this.loadResponse;
60727                             Roo.log("load?");
60728                             //this.addColumns();
60729                         },
60730                         load : function (_self, records, options)
60731                         {
60732                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
60733                                 // if you click on the translation.. you can edit it...
60734                                 var el = Roo.get(this);
60735                                 var id = el.dom.getAttribute('data-id');
60736                                 var d = el.dom.getAttribute('data-date');
60737                                 var t = el.dom.getAttribute('data-time');
60738                                 //var id = this.child('span').dom.textContent;
60739                                 
60740                                 //Roo.log(this);
60741                                 Pman.Dialog.CourseCalendar.show({
60742                                     id : id,
60743                                     when_d : d,
60744                                     when_t : t,
60745                                     productitem_active : id ? 1 : 0
60746                                 }, function() {
60747                                     _this.grid.ds.load({});
60748                                 });
60749                            
60750                            });
60751                            
60752                            _this.panel.fireEvent('resize', [ '', '' ]);
60753                         }
60754                     },
60755                     loadResponse : function(o, success, response){
60756                             // this is overridden on before load..
60757                             
60758                             Roo.log("our code?");       
60759                             //Roo.log(success);
60760                             //Roo.log(response)
60761                             delete this.activeRequest;
60762                             if(!success){
60763                                 this.fireEvent("loadexception", this, o, response);
60764                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60765                                 return;
60766                             }
60767                             var result;
60768                             try {
60769                                 result = o.reader.read(response);
60770                             }catch(e){
60771                                 Roo.log("load exception?");
60772                                 this.fireEvent("loadexception", this, o, response, e);
60773                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60774                                 return;
60775                             }
60776                             Roo.log("ready...");        
60777                             // loop through result.records;
60778                             // and set this.tdate[date] = [] << array of records..
60779                             _this.tdata  = {};
60780                             Roo.each(result.records, function(r){
60781                                 //Roo.log(r.data);
60782                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
60783                                     _this.tdata[r.data.when_dt.format('j')] = [];
60784                                 }
60785                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
60786                             });
60787                             
60788                             //Roo.log(_this.tdata);
60789                             
60790                             result.records = [];
60791                             result.totalRecords = 6;
60792                     
60793                             // let's generate some duumy records for the rows.
60794                             //var st = _this.dateField.getValue();
60795                             
60796                             // work out monday..
60797                             //st = st.add(Date.DAY, -1 * st.format('w'));
60798                             
60799                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60800                             
60801                             var firstOfMonth = date.getFirstDayOfMonth();
60802                             var days = date.getDaysInMonth();
60803                             var d = 1;
60804                             var firstAdded = false;
60805                             for (var i = 0; i < result.totalRecords ; i++) {
60806                                 //var d= st.add(Date.DAY, i);
60807                                 var row = {};
60808                                 var added = 0;
60809                                 for(var w = 0 ; w < 7 ; w++){
60810                                     if(!firstAdded && firstOfMonth != w){
60811                                         continue;
60812                                     }
60813                                     if(d > days){
60814                                         continue;
60815                                     }
60816                                     firstAdded = true;
60817                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
60818                                     row['weekday'+w] = String.format(
60819                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
60820                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
60821                                                     d,
60822                                                     date.format('Y-m-')+dd
60823                                                 );
60824                                     added++;
60825                                     if(typeof(_this.tdata[d]) != 'undefined'){
60826                                         Roo.each(_this.tdata[d], function(r){
60827                                             var is_sub = '';
60828                                             var deactive = '';
60829                                             var id = r.id;
60830                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
60831                                             if(r.parent_id*1>0){
60832                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
60833                                                 id = r.parent_id;
60834                                             }
60835                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
60836                                                 deactive = 'de-act-link';
60837                                             }
60838                                             
60839                                             row['weekday'+w] += String.format(
60840                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
60841                                                     id, //0
60842                                                     r.product_id_name, //1
60843                                                     r.when_dt.format('h:ia'), //2
60844                                                     is_sub, //3
60845                                                     deactive, //4
60846                                                     desc // 5
60847                                             );
60848                                         });
60849                                     }
60850                                     d++;
60851                                 }
60852                                 
60853                                 // only do this if something added..
60854                                 if(added > 0){ 
60855                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
60856                                 }
60857                                 
60858                                 
60859                                 // push it twice. (second one with an hour..
60860                                 
60861                             }
60862                             //Roo.log(result);
60863                             this.fireEvent("load", this, o, o.request.arg);
60864                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
60865                         },
60866                     sortInfo : {field: 'when_dt', direction : 'ASC' },
60867                     proxy : {
60868                         xtype: 'HttpProxy',
60869                         xns: Roo.data,
60870                         method : 'GET',
60871                         url : baseURL + '/Roo/Shop_course.php'
60872                     },
60873                     reader : {
60874                         xtype: 'JsonReader',
60875                         xns: Roo.data,
60876                         id : 'id',
60877                         fields : [
60878                             {
60879                                 'name': 'id',
60880                                 'type': 'int'
60881                             },
60882                             {
60883                                 'name': 'when_dt',
60884                                 'type': 'string'
60885                             },
60886                             {
60887                                 'name': 'end_dt',
60888                                 'type': 'string'
60889                             },
60890                             {
60891                                 'name': 'parent_id',
60892                                 'type': 'int'
60893                             },
60894                             {
60895                                 'name': 'product_id',
60896                                 'type': 'int'
60897                             },
60898                             {
60899                                 'name': 'productitem_id',
60900                                 'type': 'int'
60901                             },
60902                             {
60903                                 'name': 'guid',
60904                                 'type': 'int'
60905                             }
60906                         ]
60907                     }
60908                 },
60909                 toolbar : {
60910                     xtype: 'Toolbar',
60911                     xns: Roo,
60912                     items : [
60913                         {
60914                             xtype: 'Button',
60915                             xns: Roo.Toolbar,
60916                             listeners : {
60917                                 click : function (_self, e)
60918                                 {
60919                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60920                                     sd.setMonth(sd.getMonth()-1);
60921                                     _this.monthField.setValue(sd.format('Y-m-d'));
60922                                     _this.grid.ds.load({});
60923                                 }
60924                             },
60925                             text : "Back"
60926                         },
60927                         {
60928                             xtype: 'Separator',
60929                             xns: Roo.Toolbar
60930                         },
60931                         {
60932                             xtype: 'MonthField',
60933                             xns: Roo.form,
60934                             listeners : {
60935                                 render : function (_self)
60936                                 {
60937                                     _this.monthField = _self;
60938                                    // _this.monthField.set  today
60939                                 },
60940                                 select : function (combo, date)
60941                                 {
60942                                     _this.grid.ds.load({});
60943                                 }
60944                             },
60945                             value : (function() { return new Date(); })()
60946                         },
60947                         {
60948                             xtype: 'Separator',
60949                             xns: Roo.Toolbar
60950                         },
60951                         {
60952                             xtype: 'TextItem',
60953                             xns: Roo.Toolbar,
60954                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60955                         },
60956                         {
60957                             xtype: 'Fill',
60958                             xns: Roo.Toolbar
60959                         },
60960                         {
60961                             xtype: 'Button',
60962                             xns: Roo.Toolbar,
60963                             listeners : {
60964                                 click : function (_self, e)
60965                                 {
60966                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60967                                     sd.setMonth(sd.getMonth()+1);
60968                                     _this.monthField.setValue(sd.format('Y-m-d'));
60969                                     _this.grid.ds.load({});
60970                                 }
60971                             },
60972                             text : "Next"
60973                         }
60974                     ]
60975                 },
60976                  
60977             }
60978         };
60979         
60980         *//*
60981  * Based on:
60982  * Ext JS Library 1.1.1
60983  * Copyright(c) 2006-2007, Ext JS, LLC.
60984  *
60985  * Originally Released Under LGPL - original licence link has changed is not relivant.
60986  *
60987  * Fork - LGPL
60988  * <script type="text/javascript">
60989  */
60990  
60991 /**
60992  * @class Roo.LoadMask
60993  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60994  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60995  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60996  * element's UpdateManager load indicator and will be destroyed after the initial load.
60997  * @constructor
60998  * Create a new LoadMask
60999  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61000  * @param {Object} config The config object
61001  */
61002 Roo.LoadMask = function(el, config){
61003     this.el = Roo.get(el);
61004     Roo.apply(this, config);
61005     if(this.store){
61006         this.store.on('beforeload', this.onBeforeLoad, this);
61007         this.store.on('load', this.onLoad, this);
61008         this.store.on('loadexception', this.onLoadException, this);
61009         this.removeMask = false;
61010     }else{
61011         var um = this.el.getUpdateManager();
61012         um.showLoadIndicator = false; // disable the default indicator
61013         um.on('beforeupdate', this.onBeforeLoad, this);
61014         um.on('update', this.onLoad, this);
61015         um.on('failure', this.onLoad, this);
61016         this.removeMask = true;
61017     }
61018 };
61019
61020 Roo.LoadMask.prototype = {
61021     /**
61022      * @cfg {Boolean} removeMask
61023      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61024      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61025      */
61026     /**
61027      * @cfg {String} msg
61028      * The text to display in a centered loading message box (defaults to 'Loading...')
61029      */
61030     msg : 'Loading...',
61031     /**
61032      * @cfg {String} msgCls
61033      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61034      */
61035     msgCls : 'x-mask-loading',
61036
61037     /**
61038      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61039      * @type Boolean
61040      */
61041     disabled: false,
61042
61043     /**
61044      * Disables the mask to prevent it from being displayed
61045      */
61046     disable : function(){
61047        this.disabled = true;
61048     },
61049
61050     /**
61051      * Enables the mask so that it can be displayed
61052      */
61053     enable : function(){
61054         this.disabled = false;
61055     },
61056     
61057     onLoadException : function()
61058     {
61059         Roo.log(arguments);
61060         
61061         if (typeof(arguments[3]) != 'undefined') {
61062             Roo.MessageBox.alert("Error loading",arguments[3]);
61063         } 
61064         /*
61065         try {
61066             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61067                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61068             }   
61069         } catch(e) {
61070             
61071         }
61072         */
61073     
61074         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61075     },
61076     // private
61077     onLoad : function()
61078     {
61079         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61080     },
61081
61082     // private
61083     onBeforeLoad : function(){
61084         if(!this.disabled){
61085             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61086         }
61087     },
61088
61089     // private
61090     destroy : function(){
61091         if(this.store){
61092             this.store.un('beforeload', this.onBeforeLoad, this);
61093             this.store.un('load', this.onLoad, this);
61094             this.store.un('loadexception', this.onLoadException, this);
61095         }else{
61096             var um = this.el.getUpdateManager();
61097             um.un('beforeupdate', this.onBeforeLoad, this);
61098             um.un('update', this.onLoad, this);
61099             um.un('failure', this.onLoad, this);
61100         }
61101     }
61102 };/*
61103  * Based on:
61104  * Ext JS Library 1.1.1
61105  * Copyright(c) 2006-2007, Ext JS, LLC.
61106  *
61107  * Originally Released Under LGPL - original licence link has changed is not relivant.
61108  *
61109  * Fork - LGPL
61110  * <script type="text/javascript">
61111  */
61112
61113
61114 /**
61115  * @class Roo.XTemplate
61116  * @extends Roo.Template
61117  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61118 <pre><code>
61119 var t = new Roo.XTemplate(
61120         '&lt;select name="{name}"&gt;',
61121                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61122         '&lt;/select&gt;'
61123 );
61124  
61125 // then append, applying the master template values
61126  </code></pre>
61127  *
61128  * Supported features:
61129  *
61130  *  Tags:
61131
61132 <pre><code>
61133       {a_variable} - output encoded.
61134       {a_variable.format:("Y-m-d")} - call a method on the variable
61135       {a_variable:raw} - unencoded output
61136       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61137       {a_variable:this.method_on_template(...)} - call a method on the template object.
61138  
61139 </code></pre>
61140  *  The tpl tag:
61141 <pre><code>
61142         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61143         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61144         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61145         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61146   
61147         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61148         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61149 </code></pre>
61150  *      
61151  */
61152 Roo.XTemplate = function()
61153 {
61154     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61155     if (this.html) {
61156         this.compile();
61157     }
61158 };
61159
61160
61161 Roo.extend(Roo.XTemplate, Roo.Template, {
61162
61163     /**
61164      * The various sub templates
61165      */
61166     tpls : false,
61167     /**
61168      *
61169      * basic tag replacing syntax
61170      * WORD:WORD()
61171      *
61172      * // you can fake an object call by doing this
61173      *  x.t:(test,tesT) 
61174      * 
61175      */
61176     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61177
61178     /**
61179      * compile the template
61180      *
61181      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61182      *
61183      */
61184     compile: function()
61185     {
61186         var s = this.html;
61187      
61188         s = ['<tpl>', s, '</tpl>'].join('');
61189     
61190         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61191             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61192             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61193             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61194             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61195             m,
61196             id     = 0,
61197             tpls   = [];
61198     
61199         while(true == !!(m = s.match(re))){
61200             var forMatch   = m[0].match(nameRe),
61201                 ifMatch   = m[0].match(ifRe),
61202                 execMatch   = m[0].match(execRe),
61203                 namedMatch   = m[0].match(namedRe),
61204                 
61205                 exp  = null, 
61206                 fn   = null,
61207                 exec = null,
61208                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61209                 
61210             if (ifMatch) {
61211                 // if - puts fn into test..
61212                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61213                 if(exp){
61214                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61215                 }
61216             }
61217             
61218             if (execMatch) {
61219                 // exec - calls a function... returns empty if true is  returned.
61220                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61221                 if(exp){
61222                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61223                 }
61224             }
61225             
61226             
61227             if (name) {
61228                 // for = 
61229                 switch(name){
61230                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61231                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61232                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61233                 }
61234             }
61235             var uid = namedMatch ? namedMatch[1] : id;
61236             
61237             
61238             tpls.push({
61239                 id:     namedMatch ? namedMatch[1] : id,
61240                 target: name,
61241                 exec:   exec,
61242                 test:   fn,
61243                 body:   m[1] || ''
61244             });
61245             if (namedMatch) {
61246                 s = s.replace(m[0], '');
61247             } else { 
61248                 s = s.replace(m[0], '{xtpl'+ id + '}');
61249             }
61250             ++id;
61251         }
61252         this.tpls = [];
61253         for(var i = tpls.length-1; i >= 0; --i){
61254             this.compileTpl(tpls[i]);
61255             this.tpls[tpls[i].id] = tpls[i];
61256         }
61257         this.master = tpls[tpls.length-1];
61258         return this;
61259     },
61260     /**
61261      * same as applyTemplate, except it's done to one of the subTemplates
61262      * when using named templates, you can do:
61263      *
61264      * var str = pl.applySubTemplate('your-name', values);
61265      *
61266      * 
61267      * @param {Number} id of the template
61268      * @param {Object} values to apply to template
61269      * @param {Object} parent (normaly the instance of this object)
61270      */
61271     applySubTemplate : function(id, values, parent)
61272     {
61273         
61274         
61275         var t = this.tpls[id];
61276         
61277         
61278         try { 
61279             if(t.test && !t.test.call(this, values, parent)){
61280                 return '';
61281             }
61282         } catch(e) {
61283             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61284             Roo.log(e.toString());
61285             Roo.log(t.test);
61286             return ''
61287         }
61288         try { 
61289             
61290             if(t.exec && t.exec.call(this, values, parent)){
61291                 return '';
61292             }
61293         } catch(e) {
61294             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61295             Roo.log(e.toString());
61296             Roo.log(t.exec);
61297             return ''
61298         }
61299         try {
61300             var vs = t.target ? t.target.call(this, values, parent) : values;
61301             parent = t.target ? values : parent;
61302             if(t.target && vs instanceof Array){
61303                 var buf = [];
61304                 for(var i = 0, len = vs.length; i < len; i++){
61305                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61306                 }
61307                 return buf.join('');
61308             }
61309             return t.compiled.call(this, vs, parent);
61310         } catch (e) {
61311             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61312             Roo.log(e.toString());
61313             Roo.log(t.compiled);
61314             return '';
61315         }
61316     },
61317
61318     compileTpl : function(tpl)
61319     {
61320         var fm = Roo.util.Format;
61321         var useF = this.disableFormats !== true;
61322         var sep = Roo.isGecko ? "+" : ",";
61323         var undef = function(str) {
61324             Roo.log("Property not found :"  + str);
61325             return '';
61326         };
61327         
61328         var fn = function(m, name, format, args)
61329         {
61330             //Roo.log(arguments);
61331             args = args ? args.replace(/\\'/g,"'") : args;
61332             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61333             if (typeof(format) == 'undefined') {
61334                 format= 'htmlEncode';
61335             }
61336             if (format == 'raw' ) {
61337                 format = false;
61338             }
61339             
61340             if(name.substr(0, 4) == 'xtpl'){
61341                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61342             }
61343             
61344             // build an array of options to determine if value is undefined..
61345             
61346             // basically get 'xxxx.yyyy' then do
61347             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61348             //    (function () { Roo.log("Property not found"); return ''; })() :
61349             //    ......
61350             
61351             var udef_ar = [];
61352             var lookfor = '';
61353             Roo.each(name.split('.'), function(st) {
61354                 lookfor += (lookfor.length ? '.': '') + st;
61355                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61356             });
61357             
61358             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61359             
61360             
61361             if(format && useF){
61362                 
61363                 args = args ? ',' + args : "";
61364                  
61365                 if(format.substr(0, 5) != "this."){
61366                     format = "fm." + format + '(';
61367                 }else{
61368                     format = 'this.call("'+ format.substr(5) + '", ';
61369                     args = ", values";
61370                 }
61371                 
61372                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61373             }
61374              
61375             if (args.length) {
61376                 // called with xxyx.yuu:(test,test)
61377                 // change to ()
61378                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61379             }
61380             // raw.. - :raw modifier..
61381             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61382             
61383         };
61384         var body;
61385         // branched to use + in gecko and [].join() in others
61386         if(Roo.isGecko){
61387             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61388                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61389                     "';};};";
61390         }else{
61391             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61392             body.push(tpl.body.replace(/(\r\n|\n)/g,
61393                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61394             body.push("'].join('');};};");
61395             body = body.join('');
61396         }
61397         
61398         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61399        
61400         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61401         eval(body);
61402         
61403         return this;
61404     },
61405
61406     applyTemplate : function(values){
61407         return this.master.compiled.call(this, values, {});
61408         //var s = this.subs;
61409     },
61410
61411     apply : function(){
61412         return this.applyTemplate.apply(this, arguments);
61413     }
61414
61415  });
61416
61417 Roo.XTemplate.from = function(el){
61418     el = Roo.getDom(el);
61419     return new Roo.XTemplate(el.value || el.innerHTML);
61420 };